Why can't I override isEqual(_ object:) in Xcode 12? - ios

I'm using a class, which is originally written in Swift, that extends NSObject from a third-party XCFramework:
open class ExampleClass: NSObject, Codable { ... }
Since this is a third-party class, I can't modify the original, so I created an extension on top of it to override isEqual(_ object:):
extension ExampleClass {
override open func isEqual(_ object: Any?) -> Bool {
return true
}
}
This is currently compiling and building fine on Xcode 11, but with the same branch on Xcode 12, I'm getting an error that says '#objc' instance method in extension of subclass of 'ExampleClass' requires iOS 13.0.0
I've found a similar issue that seems to imply that functionality can't be overridden in a Swift extension, but I'm trying to understand what's changed since migrating from Xcode 11 to 12. Was there something that changed recently that prevents this from happening? Any thoughts on working around this?

Related

Xcode 11 "'==' is only available in iOS 13.0 or newer" error

I tried to build my project in Xcode 11 and it throws 26 identical errors
<unknown>:0: error: '==' is only available in iOS 13.0 or newer
The errors happen on Compile Swift source files stage upon calling CompileSwift normal arm64 /long/path/to/MyClass.swift .... There is no context help pointing to anything within the files. The files are quite different but look harmless and does not share anything in common.
After a lot of pain, I found the 4-month old app version compiles fine. So I did git bisect and found the offending commit, and then this code:
struct Config: Equatable {
let formatDescription: CMFormatDescription
let orientation: CGImagePropertyOrientation
}
Turns out CMFormatDescription did become Equatable only in iOS 13, while the app deployment target is iOS 11. It probably felt back to [NSObject isEqual:] in Xcode 10, but it became complicated in Xcode 11. Since Swift automatically generates Equatable conformance under the hood it had troubles in pointing the exact place of the error. The solution is to add your own Equatable implementation for CMFormatDescription:
extension CMFormatDescription: Equatable {
public static func == (lhs: CMFormatDescription, rhs: CMFormatDescription) -> Bool {
return CMFormatDescriptionEqual(lhs, otherFormatDescription: rhs)
}
}

Swift Protocol in Framework

so i have this weird problem that's been bugging me for the last few hours.
I have a framework in which I created a protocol named ChatDelegate (code bellow)
public protocol ChatDelegate: class {
func chat(_ chatCollectionView: UICollectionView, didSelect message: Message)
}
and a ViewController (not in the framework), which conforms to the ChatDelegate, like so
extension ChatContainerViewController: ChatDelegate {
func chat(_ chatCollectionView: UICollectionView, didSelect message: Message) {
print("did select")
}
}
but the compiler still complains that the ChatContainerViewController does not conform to the protocol and I don't understand why?? The function has the exact same header (I also tried putting public in front ...didn't help).
Any help would be much appreciated.
UPDATE
I figured it out. The problem was that I had Message class in my project and in the framework and the compiler didn't know which one to choose. Adding ModuleName in front (ModuleName.Message) fixed it. :D
I had the same issue. The file that defined the protocol had a target membership in both the framework and the application targets. I solved the issue by making the file that defined the protocol only have a target membership in the framework and then adding an import <FrameworkName> to the code in the application target that needed to use the protocol.

UIView/UICoordinateSpace convert rect/point clash in swift 3

So I've recently had to convert my project to Swift 3, and while a bit of a pain, it has mostly gone ok. However I am now down to one final set of errors that I can't work out!
Im using
self.convert(point, to: v)
to convert a point between views, but Xcode keeps giving me build errors saying that
'convert(_:to:)' is only available on iOS 8.0 or newer
This seems to be due to the method definition
public protocol UICoordinateSpace : NSObjectProtocol {
#available(iOS 8.0, *)
public func convert(_ point: CGPoint, to coordinateSpace: UICoordinateSpace) -> CGPoint
...
}
clashing with the normal UIView one
extension UIView {
...
open func convert(_ point: CGPoint, to view: UIView?) -> CGPoint
...
}
I can't for the life of me work out how to resolve this! Has anyone come across this or have any idea how to fix it?!
You can manually set the iOS Deployment Target to 7.0, and Xcode builds the app as targeted to 7.0. (I don't know apps built with that manner can be approved for App Store.)
So, you can test that behaviour with your Xcode 8.
And you can silence Xcode with this:
self.convert(point, to: v as UIView?)
With new renaming rule of Swift 3, some imported methods may have the same signature in Swift, and I'm afraid some of them may not have this sort of workaround, and may not be solved by changing target version.
Ok, if anyone else has this issue, turns out it's because pre-iOS 8 is no longer supported. If you update your project to recommended settings, it will set your minimum iOS version to 8 and everything will be happy :)

Compile and runtime failures when importing interfaces with category extensions in XCode 7

I'm trying to get an example of running OpenEars with the RapidEars plugin running in Swift 2.2 (XCode 7.3.1). However, I suspect I'm having a larger issue with using Objective-C interfaces with extensions in a Swift project (or my understanding of how that works).
The OpenEars code is Obj-C. However I was able to get it running in my swift project through the standard Obj-C -> Swift translation techniques.
Abbreviated code follows. The full example is on a forked Github and updated to Swift-2.2: https://github.com/SuperTango/OpenEars-with-Swift-
This following example is working great. You can see the entire project by checkng out the "working-opears-swift2.2" tag.
OpenEarsTest-Bridging-Header.h:
#import <OpenEars/OELanguageModelGenerator.h>
#import <OpenEars/OEAcousticModel.h>
#import <OpenEars/OEPocketsphinxController.h>
#import <OpenEars/OEAcousticModel.h>
#import <OpenEars/OEEventsObserver.h>
ViewController.swift:
class ViewController: UIViewController, OEEventsObserverDelegate {
var openEarsEventsObserver = OEEventsObserver()
override func viewDidLoad() {
super.viewDidLoad()
loadOpenEars()
}
func loadOpenEars() {
self.openEarsEventsObserver = OEEventsObserver()
self.openEarsEventsObserver.delegate = self
var lmGenerator: OELanguageModelGenerator = OELanguageModelGenerator()
addWords()
var name = "LanguageModelFileStarSaver"
lmGenerator.generateLanguageModelFromArray(words, withFilesNamed: name, forAcousticModelAtPath: OEAcousticModel.pathToModel("AcousticModelEnglish"))
lmPath = lmGenerator.pathToSuccessfullyGeneratedLanguageModelWithRequestedName(name)
dicPath = lmGenerator.pathToSuccessfullyGeneratedDictionaryWithRequestedName(name)
}
func startListening() {
do {
try OEPocketsphinxController.sharedInstance().setActive(true)
OEPocketsphinxController.sharedInstance().startListeningWithLanguageModelAtPath(lmPath, dictionaryAtPath: dicPath, acousticModelAtPath: OEAcousticModel.pathToModel("AcousticModelEnglish"), languageModelIsJSGF: false)
} catch {
NSLog("Error!")
}
}
// A whole bunch more OEEventsObserverDelegate methods that are all working fine...
func pocketsphinxDidStartListening() {
print("Pocketsphinx is now listening.")
statusTextView.text = "Pocketsphinx is now listening."
}
Up until this point, everything is working great.
However, In order to use the "RapidEars" plugin, the documentation (http://www.politepix.com/rapidears/) says to:
Add the framework to the project and ensure it's being included properly.
import two new files (that are both "categories" to existing OpenEars classes):
#import <RapidEarsDemo/OEEventsObserver+RapidEars.h>
#import <RapidEarsDemo/OEPocketsphinxController+RapidEars.h>
Change methods that used: startListeningWithLanguageModelAtPath to use startRealtimeListeningWithLanguageModelAtPath
add two new OEEventsObservableDelegate methods.
func rapidEarsDidReceiveLiveSpeechHypothesis(hypothesis: String!, recognitionScore: String!)
func rapidEarsDidReceiveFinishedSpeechHypothesis(hypothesis: String!, recognitionScore: String!)
The new code can be found by checking out the rapidears-notworking-stackoverflow tag from the above github repo
Problem 1:
When doing completion in the XCode editor, the editor sees WILL perform autocompletion on the startRealtimeListeningWithLanguageModelAtPath method, however when the code is run, it always fails with the error:
[OEPocketsphinxController startRealtimeListeningWithLanguageModelAtPath:dictionaryAtPath:acousticModelAtPath:]: unrecognized selector sent to instance 0x7fa27a7310e0
Problem 2:
When doing auto completion in the XCode editor, it doesn't see the two new delegate methods defined in RapidEarsDemo/OEPocketsphinxController+RapidEars.h.
I have a feeling that these are related, and also related to the fact that they failing methods are defined as Categories to Objective-C classes. But that's only a guess at this point.
I've made sure that the RapidEars framework is imported and in the framework search path.
Can anyone tell me why this is happening? Or if there's some Swift magic incantation that I missed?
The problem could be the one described in the link below, where category methods in a static library produce selector not recognized runtime errors.
Technical Q&A QA1490: Building Objective-C static libraries with categories

How to have multiple definitions for a function in swift [duplicate]

I am starting to learn Swift, and have been following the very good Stanford University video lectures on YouTube. Here is a link if you are interested or it helps (although it isn't required to understand my problem):
Developing iOS 8 Apps with Swift - 2. More Xcode and Swift, MVC
While following the lectures I got to a point where (as far as I could tell) my code was identical to the code in the video but on my system I got a compiler error. After a lot of trial and error I have managed to reduce my code to two examples, one of which generates an error, the other or which doesn't, but I have no idea what is actually causing the error or how to resolve it.
The code which creates the error is:
import UIKit
class BugViewController: UIViewController
{
func perform(operation: (Double) -> Double) {
}
func perform(operation: (Double, Double) -> Double) {
}
}
This creates the following compiler error:
Method 'perform' with Objective-C selector 'perform: ' conflicts with previous declaration with the same Objective-C selector
By simply removing the sub-classing of UIViewController the code compiles:
import UIKit
class BugViewController
{
func perform(operation: (Double) -> Double) {
}
func perform(operation: (Double, Double) -> Double) {
}
}
Some other information which may or may not be relevant:
I have recently upgraded to Yosemite.
When I installed Xcode, I ended up with a Beta version (Version 6.3 (6D543q)) because (if I remember correctly) this was the version I needed to run on my version of OS X.
I am half hoping this is a bug in the compiler because otherwise this doesn't make any sense to me. Any help very gratefully received!
I myself am also taking the Standford course and I got stuck here for a long time too, but after some searching, I found something from here: Xcode release notes and it mentioned something below:
Swift 1.2 is strict about checking type-based overloading of #objc
methods and initializers, something not supported by Objective-C.
// Has the Objective-C selector "performOperation:".
func performOperation(op: NSOperation) { /* do something */ }
// Also has the selector "performOperation:".
func performOperation(fn: () -> Void) {
self.performOperation(NSBlockOperation(block: fn))
}
This code would work when invoked from Swift, but could easily crash
if invoked from Objective-C. To solve this problem, use a type that is
not supported by Objective-C to prevent the Swift compiler from
exposing the member to the Objective-C runtime:
If it makes sense, mark the member as private to disable inference of #objc.
Otherwise, use a dummy parameter with a default value, for
example: _ nonobjc: () = (). (19826275)
Overrides of methods exposed
to Objective-C in private subclasses are not inferred to be #objc,
causing the Swift compiler to crash. Explicitly add the #objc
attribute to any such overriding methods. (19935352)
Symbols from SDKs are not available when using Open Quickly in a
project or workspace that uses Swift. (20349540)
what i did was just adding "private" in front of the override method like this:
private func performOperation(operation: Double -> Double) {
if operandStack.count >= 1 {
displayValue = operation(operandStack.removeLast())
enter()
}
}
Objective-C does not support method overloading, you have to use a different method name. When you inherited UIViewController you inherited NSObject and made the class interopable to Obj-C. Swift on the other hand does support overloading, that's why it works when you remove the inheritance.
As it has already been answered, ObjC doesn't support method overloading (two methods with the same name) and In swift 2 under Xcode 7 there are two options to solve this kind of problems. One option is to rename the method using the attribute: #objc(newNameMethod:)
func methodOne(par1, par2) {...}
#objc(methodTwo:)
func methodOne(par1) {...}
another option to solve this problem in Xcode 7+ is by applying #nonobjc attribute to any method, subscript or initialiser
func methodOne() {...}
#nonobjc
func methodOne() {...}
The problem is UIViewController is an #objc class. When inheriting from UIViewController, BugViewController is also a #objc class.
This means it must conform to the rules of Objective-C selectors (the name of a method). The methods func perform(operation: (Double) -> Double) and func perform(operation: (Double, Double) -> Double) both have the same selector #selector(perform:). This is not allowed.
To resolve this, use different names: like func perform1(operation: (Double) -> Double) and func perform2(operation: (Double, Double) -> Double).
I think the best way to handle this is to give your perform() methods more descriptive names. What do these methods do? How do they change the state of the view controller? Look at the other UIViewController methods to get a feel for the style of method naming, or read Method Names Should Be Expressive and Unique Within a Class
From https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html under "Xcode 6.3 Release Notes" -> "Swift Language Changes" you find
Swift now detects discrepancies between overloading and overriding in the Swift type system and the effective behavior seen via the Objective-C runtime.
I got the same error due to having having two methods with the same Obj-C signature:
static func prepareForUpSyncing(obj : NSManagedObject!) -> Bool
static func prepareForUpSyncing(objs : [NSManagedObject]!) -> Bool
I didn't want to mark one of them as #nonobjc due to possibility of unforseen consequences at runtime. (Someone can correct me if there is no possibility)
Resolved it by using Swift's external parameter name feature (I made external name same as local name) to the second method, which effectively changes the Obj-c method signature:
static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {

Resources