Initialize an AudioConverterRef Swift 3.0 - swift

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.

Related

Why can ids not be ported from Objective-C to Swift via struct?

If I define an Objective-C id, in a struct in a header file:
struct MyStruct {
id field;
};
struct MyStruct *_Nonnull myFunction(void);
Then I #include this header into the bridging header then attempt to use myFunction() from the Swift side, it will return OpaquePointer. If I attempt to return MyStruct directly, it will error saying myFunction is not defined. This only occurs if MyStruct contains an Objective-C id.
What is strange is if I declare a normal variable of type id, it can be ported to Swift just fine.
Why does Swift refuse structs containing Objective-C ids and what should I do to circumvent this?
Swift can't handle strong references in structs. It can't insert the required retains/releases.
You need to tell Swift it doesn't have to by marking the field __unsafe_unretained:
struct MyStruct {
__unsafe_unretained id field;
};
With that, it will be imported normally, but field will be Unmanaged, which means you'll need to handle memory management yourself as needed.
Note that if this code is also called from ObjC, then this will change its memory management there, too. As discussed in the thread, you may need to check for the __swift__ define to check what language is compiling this header.

Stored properties with #available is not possible

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

Undocumented API changes in CoreBluetooth

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

Objective-C method not visible in Swift when using Swift Enum

I have a method that uses a forward declaration of a Swift enum. Whenever I do this my method isn't visible in other Swift classes. Yet if I do a forward declaration for a class it is visible in Swift. Why is it not visible for an enum and how can I get around this?
// Objective-C Header file
#class ViewController;
typedef NS_ENUM(NSInteger, MyEnumType);
#interface ObjcViewController : UIViewController
- (void)doThis: (enum MyEnumType)type;
- (void)grabTheClass: (ViewController *)mySwiftClass;
- (void)doSomethingElse;
#end
//Swift File
#objc enum MyEnumType: Int {
case one = 1
case two = 2
}
#objc class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let controller = ObjcViewController()
controller.doSomethingElse()
controller.grabTheClass(self)
controller.doThis(EnumType) //ERROR: This will not compile
}
}
The forward declaration of the enum creates an incomplete type. And an incomplete type has very little usefulness. You can form a pointer to it, or you can call a function with a pointer to it, but that's it.
Unless you complete the enum declaration with the concrete definition, you won't be able to properly consume it, neither in Objective-C, nor Swift.
And this is why the Swift code can't see the method, because at the time the Swift compiler processes the bridging header (and this happens before any Swift code is processed), the enum is not yet materialised, thus is excluded from the bridged interface due to being an incomplete type.
If you add a method that takes a pointer to the enum:
- (void)doThisWithPointer:(enum MyEnumType *)type;
then the method will be accessible from Swift, though it will be imported as
func doThis(withPointer type: OpaquePointer)
which doesn't make it much more helpful either.
But if forward declarations are incomplete types, why does the #class works? Its because in Objective-C objects are passed as pointers, and pointers to incomplete types are usable at call sites (as we just saw). Thus, the method is available to Swift.
More, the #class statement is part of the Objective-C additions to the C language, which means the compiler can give them special treatment, like importing the method with the expected signature, instead of the OpaquePointer one. This thanks to the dynamic nature of the OOP additions over the C language.
It looks like doThis is a function, but you are missing the parentheses in the call. The line controller.doThis should instead read controller.doThis()
See if that helps.
Somebody else claimed in another post that the Swift enum needed to be declared as public in order to be visible in Objective-C. I haven't verified that however.
Edit:
Okay, after some experimentation and googling, I think you have to declare your enum in Objective-C using the NSEnum macro, and that makes it available in both languages.
I don't think Objective-C can "see" Swift Enums when they're defined in Swift.

Type and declaration restrictions on Swift capture specifiers

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