Swift CMutablePointers in factories e.g. NewMusicSequence - swift

How do you use C level factory methods in Swift?
Let's try using a factory such as NewMusicSequence().
var status:OSStatus
var sequence:MusicSequence
status=NewMusicSequence(&sequence)
This errors out with "error: variable 'sequence' passed by reference before being initialized".
Set sequence to nil, and you get EXC_BAD_INSTRUCTION.
You can try being explicit like this:
var sp:CMutablePointer<MusicSequence>=nil
status=NewMusicSequence(sp)
But then you get a bad access exception when you set sp to nil. If you don't set sp, you get an "error: variable 'sp' used before being initialized"
I'd expect this to be the way to do it, but it's not:
import AudioToolbox
var sequence: MusicSequence?
var status:OSStatus = NewMusicSequence(&sequence)
error: cannot convert the expression's type 'OSStatus' to type 'inout MusicSequence?'
var status:OSStatus = NewMusicSequence(&sequence)
Here's the reference.

I think the issue is that you need an optional value. Optional values are always initialized to nil and only optional values can ever be nil.
var sequence: MusicSequence?
let status = NewMusicSequence(&sequence)

This works for me in an Xcode project. It seems to throw EXC_BAD_ACCESS in the playground for some reason.
import Foundation
import AudioToolbox
#objc class SwiftSequenceGenerator : NSObject
{
#objc func createMusicSequence() -> MusicSequence
{
var status : OSStatus = 0
var sequence : MusicSequence = MusicSequence()
status = NewMusicSequence(&sequence)
return sequence
}
}
let generator = SwiftSequenceGenerator()
let sequence = generator.createMusicSequence()
I was testing this in an Obj-C project, hence the #objc attributes. Both this and its Obj-C counterpart were able to generate a new MusicSequence.
It is a little tricky because MusicSequence is actually a struct. In Obj-C it is defined as a pointer OpaqueMusicSequence and Swift uses COpaquePointer. They are basically the same, using a void * to pass the MusicSequence struct to the factory method to be mutated.

Related

Can't assign value of type Dictionary to LazyMapCollection

I am working on an application and I am relatively new to Swift where I am trying to initialize answerKeys with the keys of answer variable with the following code but it is showing an error.
Cannot assign value of type 'Dictionary<IntPoint, String>.Keys' to type 'LazyMapCollection<Dictionary<IntPoint, String>, IntPoint>' (aka 'LazyMapSequence<Dictionary<IntPoint, String>, IntPoint>')
I have gone through the documentation but couldn't fix this.
var answer:[IntPoint:String] = [:]
var answerKeys:LazyMapCollection<Dictionary<IntPoint,String>,IntPoint>
init() {
answerKeys = answer.keys
}
It may be that Dictionary.keys returned a LazyMapCollection in earlier Swift versions. In Swift 5 it is Dictionary<Key, Value>.Keys as can be seen from the documentation, in your case
var answerKeys: Dictionary<IntPoint, String>.Keys
But note that you can always access answer.keys in your code instead of assigning this to a separate property.

Optional value nil found after using AudioFileGetProperty() with kAudioFilePropertyDataFormat

I'm trying to use swift AudioToolbox API to playback an audio file. However, when I get the AudioStreamBasicDescription using AudioFileGetProperty() the app crashes when I try to unwrap the value, saying "Unexpectedly found nil while unwrapping an optional value". This makes no sense because:
1) The OSStatus after I call AudioFileGetProperty() is zero.
2) I can see that the value is "nil" at first in the console, but after calling AudioFileGetProperty() it changes to "Some" and suddenly it is populated with values.
Perhaps am I using pointers incorrectly? Am I using optionals incorrectly?
func playAudioFileWithToolbox(){
var aqData = AQPlayerState()
let url2 = bundle.path(forResource: "dave_speaking", ofType: "m4a")!
var filePathArray = Array(url2.utf8)
let audioFileUrl = CFURLCreateFromFileSystemRepresentation(nil, &filePathArray, filePathArray.count, false)
//Problem: how do we pass aqData.mAudioFile? Initially it is nil
//We are supposed to pass by reference
let status = AudioFileOpenURL(audioFileUrl!, permissions, 0, &(aqData.mAudioFile))
var dataFormatSize:UInt32 = UInt32(MemoryLayout<AudioStreamBasicDescription>.size)
//Here I populate aqData.mDataFormat
var propertyStatus = AudioFileGetProperty(aqData.mAudioFile!, kAudioFilePropertyDataFormat, &dataFormatSize, &(aqData.mDataFormat))
//Next line crashes saying that it is unwrapping an optional value
var audioStreamDescription = aqData.mDataFormat!
}
struct AQPlayerState{
var mDataFormat:AudioStreamBasicDescription?
var mQueue:AudioQueueRef?
var mBuffers:AudioQueueBufferRef?
var mAudioFile: AudioFileID?
var bufferByteSize:UInt32 = 0
var mCurrentPacket:Int64 = 0
var mNumPacketsToRead:UInt32 = 0
var mPacketDescs : UnsafeMutablePointer<AudioStreamPacketDescription>?
var mIsRunning : Bool = false
init(){
}
}
So I figured out I was using a combination of pointers and optional values incorrectly. Because mDataFormat was by itself an optional:
var mDataFormat:AudioStreamBasicDescription?
passing a reference to it didn't make much sense, i.e., &mDataFormat.
Therefore I changed this variable to make it non-optional:
var mDataFormat:AudioStreamBasicDescription = AudioStreamBasicDescription()
That way, there was memory allocated to it, as well as an address, and it now made sense to do something like this:
&aqData.mDataFormat
var propertyStatus = AudioFileGetProperty(aqData.mAudioFile!, kAudioFilePropertyDataFormat, &dataFormatSize, &aqData.mDataFormat)
Anyway I am still learning how to combine optionals with pointers so any additional feedback you can give me would help.

Mutating an array declared with let keyword

I get that let is used for constants and var is used for variables. However, this piece of code has confused me.
func filterGreaterThanValue(value: Int, numbers: [Int]) -> [Int] {
let result:[Int] = [Int]()
for number in numbers {
if number > value {
result.append(number)
}
}
return result
}
Running this yields the error
error: MyPlayground.playground:5:13: error: cannot use mutating member on immutable value: 'result' is a 'let' constant
result.append(number)
^~~~~~
To my understanding, an object declared with the let keyword is immutable in the sense that I can change its properties, but cannot reassign the variable pointing to that object to a different object (ie. have it point to another address in memory).
However, in this example I'm doing the same thing right? I've initialized an array object and I'm just modifying its properties. Why am I not allowed to do this?
Thanks.
In Swift Array is a struct which is value type. To change in the properties of struct you need to make both object and properties var type.

How to use UnsafeMutablePointer<OpaquePointer> in Swift?

How does one use an UnsafeMutablePointer<OpaquePointer> in Swift with some Core Foundation framework? Why have an UnsafeMutablePointer<OpaquePointer>?
Given, general: some UnsafeMutablePointer<SomeType> where typealias SomeType = OpaquePointer
Specific Example API
// SOURCE: import ApplicationServices.PrintCore
typealias PMPrinter = OpaquePointer
func PMSessionGetCurrentPrinter(_ printSession: PMPrintSession, _ currentPrinter: UnsafeMutablePointer<PMPrinter>)
func PMPrinterGetPaperList(PMPrinter, UnsafeMutablePointer<Unmanaged<CFArray>?>)
Specific Example Use Case: get list of papers supported by a printer
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
var currentPrinterOptional: PMPrinter? = nil
PMSessionGetCurrentPrinter(printSession, &currentPrinterOptional!)
guard let currentPrinter = currentPrinterOptional else { return }
// Get the array of pre-defined PMPapers this printer supports.
// PMPrinterGetPaperList(PMPrinter, UnsafeMutablePointer<Unmanaged<CFArray>?>)
var paperListUnmanaged: Unmanaged<CFArray>?
PMPrinterGetPaperList(currentPrinter, &paperListUnmanaged)
guard let paperList = paperListUnmanaged?.takeUnretainedValue() as [AnyObject]? else { return }
Observed Errors
What compiles does not run. What seems like (maybe) reasonable syntax does not compile.
The above example gets the following (expected) Runtime "fatal error: unexpectedly found nil while unwrapping an Optional value".
Some select other attempts:
// Compile Error: Address of variable 'currentPrinter' taken before is is initialized
var currentPrinter: PMPrinter
PMSessionGetCurrentPrinter(printSession, &currentPrinter)
// Compile Error: Nil cannot initialze specified type 'PMPrinter' (aka 'OpaquePointer')
var currentPrinter: PMPrinter = nil
PMSessionGetCurrentPrinter(printSession, &currentPrinter)
// Compile Error: Variable 'currentPrinterPtr' used before being initialized
var currentPrinterPtr: UnsafeMutablePointer<PMPrinter>
PMSessionGetCurrentPrinter(printSession, currentPrinterPtr)
// Compile OK: actually compiles
// Runtime Error: unexpectedly found nil while unwrapping an Optional value
var currentPrinterOptional: PMPrinter? = nil
PMSessionGetCurrentPrinter(printSession, &currentPrinterOptional!)
Resources
Apple: Core Printing ⇗
Apple: Using Swift with Cocoa and Objective-C ⇗
While the docs have useful information, a workable implementation for UnsafeMutablePointer<PMPrinter> with typealias as UnsafeMutablePointer<OpaquePointer> has been elusive.
PMPrinter and PMPaper are defined in the PrintCore framework
as pointer to an "incomplete type"
typedef struct OpaquePMPrinter* PMPrinter;
typedef struct OpaquePMPaper* PMPaper;
Those are imported into Swift as OpaquePointer, and are a bit
cumbersome to use.
The second argument to PMSessionGetCurrentPrinter() is a pointer to
a non-optional PMPrinter variable, and in Swift it must be
initialized before being passed as an inout argument. One possible way
to initialize a null-pointer is to use unsafeBitCast.
The easiest way to get the PMPaper objects from the array seems to
be to use CFArrayGetValueAtIndex() instead of bridging it to a
Swift array. That returns a UnsafeRawPointer which can be converted
to an OpaquePointer.
This worked in my test:
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
var currentPrinter = unsafeBitCast(0, to: PMPrinter.self)
PMSessionGetCurrentPrinter(printSession, &currentPrinter);
var paperListUnmanaged: Unmanaged<CFArray>?
PMPrinterGetPaperList(currentPrinter, &paperListUnmanaged)
guard let paperList = paperListUnmanaged?.takeUnretainedValue() else {
fatalError()
}
for idx in 0..<CFArrayGetCount(paperList) {
let paper = PMPaper(CFArrayGetValueAtIndex(paperList, idx))!
var width = 0.0, height = 0.0
PMPaperGetWidth(paper, &width)
PMPaperGetHeight(paper, &height)
print(width, height)
}

Swift: CoreData - unrecognized selector sent to instance when insert row

This error is strange:
-[<>.Tips setTipName:]: unrecognized selector sent to instance <>
the name setTipName does not occur anywhere in the code but there is a variable tipName (note the lower case "t"
I am attempting to insert a row into a CoreData entity
class Tips: NSManagedObject {
#NSManaged var sectionNumber: NSNumber
#NSManaged var tipDescription: String
#NSManaged var viewName: String
#NSManaged var tipName: String
#NSManaged var tipOrder: NSNumber
#NSManaged var tipType: String
#NSManaged var tipLinkName: String
}
Here is the code doing the insert:
func createNewTips ()
{
// set create all switches and set to off
var error: NSError? = nil
var textCount = textArray.count
for var i = 0; i<textCount; ++i
{
var newTip = NSEntityDescription.insertNewObjectForEntityForName("Tips", inManagedObjectContext: managedObjectContext!) as! Tips
newTip.viewName = viewName
newTip.tipName = textArray [i].tipName
NSLog("after tipName")
newTip.tipDescription = textArray[i].tipDescription
NSLog("after set tipDescription")
newTip.tipOrder = textArray[i].tipOrder
NSLog("after set tipOrder")
newTip.sectionNumber = textArray[i].sectionNumber
NSLog("after set sectionNumber")
newTip.tipType = textArray[i].type
NSLog("after set type")
newTip.tipLinkName = textArray[i].tipLinkName
var error: NSError? = nil
if !managedObjectContext!.save(&error) {
NSLog("error &d", error!)
abort()
} // end save
} // end loop
} // end of createNewSwitches
I've recreated the data model several times
I've also changed the order of the attributes and the error occurs on a different attribute .. I've noticed that it appears to be the first attribute when I move the viewName attribute later in the list.
Here is the code in textArray
var textArray:[(sectionNumber: Int, tipOrder: Int, tipName: String, tipDescription: String, type: String, tipLinkName:String)] =
[
(1,0,"sw1","Check in and around your home for damage","text",""),
(1,1,"sw2","Dispose of any spoiled or contaminated foods, especially after a power outage. If you’re not sure, throw it out. You can check the food safety tips below","text",""),
(1,2,"sw3","Encourage family members to talk about their experience and their feelings, especially children","text",""),
(1,3,"sw4","Contact other family members to let them know that you are safe","text",""),
(2,0,"sw5","Check Utilities","link",""),
(3,0,"sw6","Food Safety Tips","link",""),
]
Any suggestions about this?
The method setTipName is an auto-generated setter method created for NSManagedObject subclasses behind the scenes. It won't appear in code even if you use the modeler to create the NSManagedObject Subclass.
Core Data has to wrap all all modeled attributes in getters and setters to ensure that key-value observing, validation etc gets trigger.The naming is automatic and follows the old Objective-C convention. There will also be either a tipName or getTipName method.
I suspect you are not actually getting a Tip object back from the insertion. I'm behind the curve on Swift but I'm good with Core Data, I don't think the "as!" cast should be needed here.
var newTip = NSEntityDescription.insertNewObjectForEntityForName("Tips", inManagedObjectContext: managedObjectContext!) as! Tips
… because the compiler should be expecting a Tips object. The empty "<>" in the error message suggest that you don't in fact have a Tips object (or did you edit the error message.)
Were this Objective-C the answer to the error would definitely be that you have the wrong class returned from the insert. The most common causes of the error are:
Failing to assign a NSManagedObject subclass name in the Core Data model editor and leaving it just a generic NSManageObject
Misspelling the class name in the model e.g. "Tip", "tips", Tipps" or some such.