In Xcode 12.3, CoreBluetooth.CBService.peripheral is defined in objective-c as:
#property(assign, readonly, nonatomic) CBPeripheral *peripheral;
Update: Here's the swift translation of the above in Xcode 12.3:
unowned(unsafe) open var peripheral: CBPeripheral { get }
In Xcode 13.0, CBService.peripheral is defined in swift as:
weak var peripheral: CBPeripheral? { get }
Apple's documentation states that this API has existing since iOS5 and there have been no changes. However in Xcode 13, the variable is clearly an optional. (And it is not an optional in Xcode 12.3 as it is missing the attribute nullable.)
The fix is relatively easy (e.g. service.peripheral -> service?.peripheral) - but it makes it impossible to use the same code for both Xcode 12.3 and 13.0. I'm wondering if there is some nuance here that that I'm missing?
Optionals are an inherent part of Swift and they are not part of Objective-C. A non-null reference in Swift in guaranteed to have a value while any reference in Objective C could, in theory, be null.
The nullable and nonnull decorators were introduced to improve interoperability with Swift with the side effect that they also more clearly document Objective C APIs.
Core Bluetooth is an Objective C API and, as you note, it has been available since iOS5; Well before Swift and the nullable decorator.
So, it isn't so much that the API has changed, but rather you are comparing the Objective C API with the Swift API and Apple has not added the nullable decorator to the Core Bluetooth API.
Neither of these APIs have changed; The Swift declaration for peripheral on CBService has always been an optional. The Objective-C declaration has never had nullable but a null value has always been possible.
Adding nullable wouldn't change the behaviour of the API, just its declaration in Objective C. This would potentially be a breaking change for Objective-C code, or at least could cause compilation to fail, so there is no reason for Apple to change it and a good reason not to.
Update
From your comment, there does, indeed, appear to have been a breaking change in the Swift declaration of CBService.peripheral from unowned(unsafe) to a weak optional.
This is a much safer declaration, since in the previous definition the onus was on you to check that peripheral wasn't nil and there was a potential crash if you didn't.
In my many years of programming with Core Bluetooth, I don't think I have ever needed to use CBService.peripheral, however, you can use conditional compilation based on the Swift version to write code that works in both Xcode 13 and earlier versions:
var p: CBPeripheral?
#if swift(<5.5)
if s.peripheral != nil {
p = s.peripheral
}
#else
p = s.peripheral
#endif
Related
I want to use a feature that is only available in >= iOS 16 therfore I use the #available flag but it does not work because "Stored properties cannot be marked potentially unavailable with '#available'"
#available(iOS 16.0, *) #State private var photoPickerItems = [PhotosPickerItem]()
In Xcode 14 Release Notes, there are resolved issues.
This is one of them.
Stored properties in Swift can’t have type information that is potentially unavailable at runtime. However, prior to Swift 5.7 the compiler incorrectly accepted #available attributes on stored properties when the property had either the lazy modifier or an attached property wrapper. This could lead to crashes for apps running on older operating systems. The Swift compiler now consistently rejects #available on all stored properties. (82713248) (FB9594187).
So you can no longer use #available on your stored property.
That makes sense, and is expected. The memory map of the object’s properties gets defined at compile-time. The compiler wants all instances of your class to contain a fixed set of properties so it knows where to find the properties in memory.
If you want properties to be available or not depending on the OS version, make those properties Optional and then write instance method code that uses an #available to either load a value into each property if the OS is available, or leave it nil if not.
Instead of declaring it as:
#available(iOS 16.0, *) #State private var photoPickerItems = [PhotosPickerItem]()
Which doesn't work, declare it as
#State private var photoPickerItems: [PhotosPickerItem]? = nil
And then in your initializer, wrap an assignment to that property in an #available. That way, except in iOS ≥ 16, the property will remain nil. In iOS ≥ 16, the #available statement will execute and your property will get a value assigned to it.
(You will then need to rewrite all the code that reads that property to unwrap it, use if let, guard let, the nil coalescing operator, or other ways of dealing with optionals. (Explaining all those is beyond the scope of this answer, and such "nuts and bolts" Swift that if you don't know how to deal with optionals, you should stop and study the subject until you do.)
I am trying to extend a class written in Obj-C and include an extension written in Swift that makes it conform to the UIDropInteractionDelegate, like so:
#available(iOS 11.0, *)
extension NoteEditViewController: UIDropInteractionDelegate {
#available(iOS 11.0, *)
public func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
let operation: UIDropOperation
if session.localDragSession == nil {
operation = .forbidden
} else {
// If a local drag session exists, we only want to move an
// existing item in the pin board to a different location.
operation = .forbidden
}
return UIDropProposal(operation: operation)
}
#objc(setupDropInteractions)
#available(iOS 11.0, *)
func setupDropInteractions() {
// Add drop interaction
self.view.addInteraction(UIDropInteraction(delegate: self))
}
}
My problem is that Project_Name-Swift.h file contains the following code that will not compile:
#class UIDropInteraction;
#protocol UIDropSession;
#class UIDropProposal;
// This line is causing the issue saying "'UIDropInteractionDelegate' is partial: introduced in iOS 11.0"
#interface NoteEditViewController (SWIFT_EXTENSION(Bloomberg_Professional)) <UIDropInteractionDelegate>
- (UIDropProposal * _Nonnull)dropInteraction:(UIDropInteraction * _Nonnull)interaction sessionDidUpdate:(id <UIDropSession> _Nonnull)session SWIFT_WARN_UNUSED_RESULT SWIFT_AVAILABILITY(ios,introduced=11.0);
- (void)setupDropInteractions SWIFT_AVAILABILITY(ios,introduced=11.0);
#end
The compiler is complaining that the interface in that file is partial.
'UIDropInteractionDelegate' is partial: introduced in iOS 11.0
I assumed that including the #available(iOS 11.0, *) would generate a SWIFT_AVAILABILITY(ios,introduced=11.0) that would encapsulate the entire interface but I was wrong.
Is there a way to fix this?
UPDATE
I implemented a toy example.
Here is the toy ViewController:
Here is the swift extension:
And here is the generated dnd_toy-Swift.h file.
You were right that it is simply a warning but my problem is that we must treat all warnings as errors in our project.
Any ideas on how to get rid of this warning from here?
You've done everything correctly, and the complaint is accurate if not extremely obtuse.
Basically, since you've (correctly) marked that function as available in iOS 11, the compiler will warn you about the use of it in any code that is targeting something < iOS 11.
So you can make the complaint go away by setting your deployment target to iOS 11, which means users of iOS 10 simply won't be allowed to install it. Or, you can use the new (to obj-c) #available construct to guard against the use of the API.
if (#available(iOS 11, *)) {
[self setupDropInteractions];
}
This construct isn't supported by Xcode 8 since it's a recent back port of the #available construct provided by Swift.
Update
I'm clarifying where I'm coming from. It seems I haven't been able to reproduce the situation that the asker is experiencing, so I'm demonstrating what I have been able to do.
I can produce 2 different compiler warnings that are similar but it seems not identical to the original question.
Here is my generated objc interface from my_project-Swift.h:
#interface NoteEditViewController (SWIFT_EXTENSION(conditional_class_declaration)) <UIDropInteractionDelegate>
- (UIDropProposal * _Nonnull)dropInteraction:(UIDropInteraction * _Nonnull)interaction sessionDidUpdate:(id <UIDropSession> _Nonnull)session SWIFT_WARN_UNUSED_RESULT SWIFT_AVAILABILITY(ios,introduced=11.0);
- (void)setupDropInteractions SWIFT_AVAILABILITY(ios,introduced=11.0);
#end
Issue 1: Declaring and objc property to conform to the protocol
Issue 2: Using a method declared in the extension
With more information to reproduce the compilation error, I'll be able to help more.
Update 2: Hopefully the last
I found that the discrepancies in behavior between us was due to the fact that my project was created with Xcode 8.3 then migrated to 9. There seems to be a difference in build settings after these things happen. The setting in question is CLANG_WARN_UNGUARDED_AVAILABILITY, which I think is new for Xcode 9.
During migration the project ends up like this:
This maps to the term YES in the .pbxproject file
After new project creation:
This maps to the term YES_AGGRESSIVE is the .pbxproject file
This setting is discussed in WWDC2017 - What's new in LLVM, but nothing they say would suggest this minor behavioral difference. I'm guessing that it is a bug with clang and how it's handling the difference between the two settings (but I'd welcome other input). As I'm sure you've figured out, this code runs fine on iOS 10. Also, if you change the setting to simply "Yes", you will still get the correct warnings about iOS 11 APIs.
In ObjC, adding API_AVAILABLE(ios(11.0)) to end of the function definition will suppress the warning. Like this:
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0))
{
... function implementation ...
}
Xcode 9 beta 4 will build, if #available is added to each method. The build still fails if #available is added only at the extension level.
var audioConverter : AudioConverterRef = nil
audioConverter = AudioConverterRef.init()
So basically I have the code above found from this StackOverflow answer that is using a previous version of Swift. Now in Swift 3.0 however the above initializer for AudioConverterRef is not available.
I noticed that the AudioConverterRef is a reference to an audio converter object, which I suppose is an AVAudioConverter.
So, the short question would be how would I write the above code in Swift 3.0? And the longer question would be what are the uses of creating an AudioConverterRef that just references an AVAudioConverter? Aren't all variables just reference to an object?
As you know, AudioConverterRef was just a typealias of COpaquePointer in Swift 2.x and is a typealias of OpaquePointer in Swift 3.
But one significant change you should realize is not the name but the feature which is common to all pointers in Swift 3:
In Swift 3, pointer types cannot contain nil, and if you want to store nil to a pointer type variable, you need to declare it as an Optional pointer. (SE-0055)
So, for the short question:
var audioConverter : AudioConverterRef? = nil
audioConverter = nil
And for the longer one:
The type AudioConverterRef is declared as:
typedef struct OpaqueAudioConverter * AudioConverterRef;
And the type struct OpaqueAudioConverter is a hidden C-struct. It is not just referencing AVAudioConverter, but may be holding some info to work with C-function based AudioConverter APIs. Its properties may be held in more primitive forms than similar properties in AVAudioConverter.
You have no need to work with AudioConverterRef, if all functionalities you need is available in AVAudioConverter.
I'm confused about the type requirements and declaration restrictions for Swift capture specifiers. The documentation says that weak references must be var and "of optional type", and that unowned references must be of non-optional type. But Apple's own API reference has examples of unowned(unsafe) references declared as optionals, while the Xcode interface builder creates weak outlets and actions that are implicitly unwrapped (which is not always clearly "of optional type" in the language reference).
What types are required for each of the Swift capture specifiers? Which must be var and which can be let?
FWIW, I think its
weak can be Type? or Type!, but not Type; and must be var
unowned(safe) (same as unowned) must be Type; and may be let
unowned(unsafe) can be Type? or Type!; and may be let
but I'm far from sure.
First the Cocoaor Cocoa touch APIs are imported in swift fromobjective-c`. They are mapped in swift with best possible ways.So that both can interoperable.
Those unowned(unsafe) properties are marked as assign in ObjC. These are unsafe and ARC doesn't retain the object, and it doesn't make sure the reference gets reset to nil if the object is deallocated. These are old apple API and not changed with ARC and remain asassignbut you should makedelegateweak`
Do not look for headers for best practices in swift they have made many tricks to make swift and objective-c interoperable and if you do what headers do than you loose all safety swift proveide.
You are right
weak should be optional as contain nil automatically if objects get deallocate and no other pointer retain it
unowned(safe) should not be optional and will not reset to nilif objects deal-located
unowned(unsafe) can or can not be optional as it not provide any safety by ARC for object deal-location and do not reset to nil.You should not use this in swift program.Use unowned if needed.It is just for interoperability.
weak is always var because it can be changed when objects deallocate and set to nil.
unowned(safe) and unowned(unsafe) can be both var or let as they are dependent on you and run-time will not change these variables.
Note:You can not declare a unowned(unsafe) as optional in swift program.Its for just interoperability and should not be used in swift.They have made this because of assign or unretain properties can be nil
I am trying to see if there is a short-hand way to write the following using Swift.
#property (nonatomic, strong) UIColor* circleColor
I have read through the documentation and I can't seem to find a way to shorten properties in general without having to writing both the setters and/or getters.
The equivalent of that in Swift would be this:
var circleColor:UIColor
which is actually a good deal shorter than the ObjC version. Swift somewhat merges the concepts of properties and instance variables.
If you need 'nil' to be a possible value for circleColor, then use 'UIColor?' instead of 'UIColor'.
I'm posting this answer to raise the visibility of #JackWu's comment - the way this should be done is:
var circleColor: UIColor!
The implicitly-unwrapped version is the recommended way and more closely represents how object properties work in Obj-C right now (i.e. the fact that they begin life as nil once declared).