Converting Objective-C block to Swift - ios

I have a function written in Objective-C below and I want to convert it to Swift, but I keep getting errors.
Below is the Objective-C code:
- (void)openFileWithFilePathURL:(NSURL*)filePathURL
{
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL];
self.filePathLabel.text = filePathURL.lastPathComponent;
//
// Plot the whole waveform
//
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
//
// Get the audio data from the audio file
//
__weak typeof (self) weakSelf = self;
[self.audioFile getWaveformDataWithCompletionBlock:^(float **waveformData,
int length)
{
[weakSelf.audioPlot updateBuffer:waveformData[0]
withBufferSize:length];
}];
}
And here is my Swift code:
func openFileWithFilePathURL(url: NSURL) {
let audioFile = EZAudioFile(URL: url)
audioPlot.plotType = EZPlotType.Buffer
audioPlot.shouldFill = true
audioPlot.shouldMirror = true
audioFile.getWaveformDataWithCompletionBlock({(waveformData, length) in
audioPlot.updateBuffer(waveformData[0], withBufferSize: length)
})
}
And I always get the error
Command failed due to signal: Segmentation fault: 11
I'm new to iOS language and I spent hours on this problem. I really have no clue on how to fix this problem.
I guess the problem lies in how I converted the block from Objective-C to Swift.
Thank you for your help!!

You can try this:
func openFileWithFilePathURL(filePathURL: NSURL) {
self.audioFile = EZAudioFile.audioFileWithURL(filePathURL)
self.filePathLabel.text = filePathURL.lastPathComponent
//
// Plot the whole waveform
//
self.audioPlot.plotType = EZPlotTypeBuffer
self.audioPlot.shouldFill = true
self.audioPlot.shouldMirror = true
//
// Get the audio data from the audio file
//
weak var weakSelf = self
self.audioFile.getWaveformDataWithCompletionBlock({(waveformData: Float, length: Int) -> Void in
weakSelf.audioPlot.updateBuffer(waveformData[0], withBufferSize: length)
})
}

This is typically an Xcode glitch. The only thing you can do it try to alter the syntax, first by the order of the lines and then the actual lines themselves (i.e. the type of line has several variations). You can also submit a bug report to apple if you can still not fix it. (here)

Related

Deprecated withUnsafeBytes [duplicate]

I previously used this code in Swift 4.2 to generate an id:
public static func generateId() throws -> UInt32 {
let data: Data = try random(bytes: 4)
let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
return value // + some other stuff
}
withUnsafeBytes is deprecated on Swift 5.0. How can I solve this?
In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:
let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }
(compare How to use Data.withUnsafeBytes in a well-defined manner? in the Swift forum). Note that this requires that the memory is aligned on a 4-byte boundary. For alternatives see round trip Swift number types to/from Data.
Note also that as of Swift 4.2 you can create a random 32-bit integer simply using the new Random API:
let randomId = UInt32.random(in: .min ... .max)
On Xcode 10.2, Swift 5, using $0.load(as:) didn't work for me, both when reading from the pointer or writing to it.
Instead, using $0.baseAddress?.assumingMemoryBound(to:) seems to work well.
Example reading from the pointer buffer (code is unrelated to the question):
var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
return
}
reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}
Example writing to the buffer pointer (code is unrelated to the question):
try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
passphrase,
passphrase.utf8.count,
salt,
salt.utf8.count,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
rounds,
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
kCCKeySizeAES256)
guard status == kCCSuccess else {
throw Error.keyDerivationError
}
}
The code from the question would look like:
let value = data.withUnsafeBytes {
$0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}
In cases where the 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) warning persists, it seems like the compiler can get confused when the closure has only one line. Making the closure have two or more lines might remove the ambiguity.
One more way to fix this warning to use bindMemory(to:).
var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
return Int32(kCCMemoryFailure)
}
return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}
I got this error as I was trying to figure out a compression stream tutorial. To get it to work, I added a step of converting the raw buffer pointer to a UnsafePointer
Original code from a tutorial I was working on.
--> where input: Data
--> where stream: compression_stream
//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in
//holder
var output = Data()
//Source and destination buffers
stream.src_ptr = srcPointer //UnsafePointer<UInt8>
stream.src_size = input.count
… etc.
}
Code with a conversion to make the above code work with a valid method
return input.withUnsafeBytes { bufferPtr in
//holder
var output = Data()
//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress
//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
return output
}
//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)
// Jump back into the original method
stream.src_ptr = typedPointer //UnsafePointer<UInt8>
}

Has anyone tried how to use vision api(VNHomographicImageRegistrationRequest) in ios 11?

I am studying currency recognition problems which is related to the Vision SDK of iOS11.
I'm having trouble handling VNHomographicImageRegistrationRequest, which determines the perspective warp matrix needed to align the content of two images. But I couldn't find how to send two images parameters into this API, can anyone help me?
Apple's Vision framework flow is always the same: Request -> Handler -> Observation
Example:
// referenceAsset & asset2 can be:
// CGImage - CIImage - URL - Data - CVPixelBuffer
// Check initializers for more info
let request = VNHomographicImageRegistrationRequest(targetedCGImage: asset2, options: [:])
let handler = VNSequenceRequestHandler()
try! handler.perform([request], on: referenceAsset)
if let results = request.results as? [VNImageHomographicAlignmentObservation] {
print("Perspective warp found: \(results.count)")
results.forEach { observation in
// A matrix with 3 rows and 3 columns.
print(observation.warpTransform)
}
}
-(matrix_float3x3)predictWithVisionFromImage:(UIImage *)imageTarget toReferenceImage:(UIImage*)imageRefer{
UIImage *scaledImageTarget = [imageTarget scaleToSize:CGSizeMake(224, 224)];
CVPixelBufferRef bufferTarget = [imageTarget pixelBufferFromCGImage:scaledImageTarget];
UIImage *scaledImageRefer = [imageRefer scaleToSize:CGSizeMake(224, 224)];
CVPixelBufferRef bufferRefer = [imageRefer pixelBufferFromCGImage:scaledImageRefer];
VNHomographicImageRegistrationRequest* request = [[VNHomographicImageRegistrationRequest alloc]initWithTargetedCVPixelBuffer:bufferTarget completionHandler:nil];
VNHomographicImageRegistrationRequest* imageRequest = (VNHomographicImageRegistrationRequest*)request;
VNImageRequestHandler* handler = [[VNImageRequestHandler alloc]initWithCVPixelBuffer:bufferRefer options:#{}];
[handler performRequests:#[imageRequest] error:nil];
NSArray* resultsArr = imageRequest.results;
VNImageHomographicAlignmentObservation* firstObservation = [resultsArr firstObject];
return firstObservation.warpTransform;
}

Is the ios iPhone simulator causing memory usage analysis to swell?

I am trying to process a large text file in my app. I know that I want to be careful with the amount of memory being consumed while I read the data. Once a piece of data is read the app does not need to keep the data around.
Thanks to “Martin R” and the post Read a file/URL line-by-line for helping me jump start my effort.
I am trying to monitor the memory consumption of my app as it reads in the large data file so that I can be sure it is behaving as expected. Here’s where I am running into a problem.
When I run Instruments using Command-I from within Xcode and I monitor allocations I see that during the read of the file the app peeks at ~15MB and then drops back down. This is fairly repeatable +/- 0.5MB.
When I run the app using Command-R from within Xcode and then let it finish reading through the file, and then press record within Instruments, the memory consumption now swells to ~360MB.
So to clarify, the two ways I have done measurement of memory allocations are:
Profile:
1. Xcode Command-I.
2. Instruments Record Allocations. Observe ~15MB
Simulate and Profile:
1. Xcode Command-R.
2. Let app run to “IDLE”.
3. Instruments Record. Observe ~360MB.
I have been trying to figure out a few things here.
Q1. Why the difference? (This may answer all my questions)
Q2. Do I have a real problem or is this only a problem because of how debug code is annotated on to the simulator?
Q3. Similar to Q2, if I run a debug build on a real device, will it have the same issue?
Q4. For my app, ~15MB is acceptable when parsing the file, but ~360MB will not be. Is there another way I can continue to debug on my device without taking this 360MB hit?
Version 8.1 (8B62)
Sierra
2.7Ghz i5
MacBook Pro Circa 2015
Sample Code attached. The first part of the file is merely a copy of the code from the referenced post for reader convenience. One can take this code as is and run it in Xcode. At the bottom is the ViewController ViewDidLoad() method where things "run". The memory “swell” is after “File opened”.
//
//
import UIKit
/* Originally from
* stackoverflow:
* https://stackoverflow.com/questions/24581517/read-a-file-url-line-by-line-in-swift
* posted by Martin R.
* Much thanks!
*/
class StreamReader {
let encoding : String.Encoding
let chunkSize : Int
var fileHandle : FileHandle!
let delimData : Data
var buffer : Data
var atEof : Bool
init?(path: String, delimiter: String = "\n", encoding: String.Encoding = .utf8,
chunkSize: Int = 4096) {
guard let fileHandle = FileHandle(forReadingAtPath: path),
let delimData = delimiter.data(using: encoding) else {
return nil
}
self.encoding = encoding
self.chunkSize = chunkSize
self.fileHandle = fileHandle
self.delimData = delimData
self.buffer = Data(capacity: chunkSize)
self.atEof = false
}
deinit {
self.close()
}
/// Return next line, or nil on EOF.
func nextLine() -> String? {
precondition(fileHandle != nil, "Attempt to read from closed file")
// Read data chunks from file until a line delimiter is found:
while !atEof {
if let range = buffer.range(of: delimData) {
// Convert complete line (excluding the delimiter) to a string:
let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding)
// Remove line (and the delimiter) from the buffer:
buffer.removeSubrange(0..<range.upperBound)
return line
}
let tmpData = fileHandle.readData(ofLength: chunkSize)
if tmpData.count > 0 {
buffer.append(tmpData)
} else {
// EOF or read error.
atEof = true
if buffer.count > 0 {
// Buffer contains last line in file (not terminated by delimiter).
let line = String(data: buffer as Data, encoding: encoding)
buffer.count = 0
return line
}
}
}
return nil
}
/// Start reading from the beginning of file.
func rewind() -> Void {
fileHandle.seek(toFileOffset: 0)
buffer.count = 0
atEof = false
}
/// Close the underlying file. No reading must be done after calling this method.
func close() -> Void {
fileHandle?.closeFile()
fileHandle = nil
}
}
extension StreamReader : Sequence {
func makeIterator() -> AnyIterator<String> {
return AnyIterator {
return self.nextLine()
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let path2WordList = Bundle.main.path(forResource: "large_text_file", ofType: "txt")
var wordCnt: Int = 0
if nil != path2WordList {
if let aStreamReader = StreamReader(path: path2WordList!) {
defer { aStreamReader.close() }
print("File openned")
/* Read and discard */
while aStreamReader.nextLine() != nil {
wordCnt += 1
}
} // if let ...
} // if nil ...
print ("Final wordCnt := \(wordCnt)")
} // viewDidLoad
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I've encountered problems like this when using long running while loops. The problem is that anything that is allocated into the current autorelease pool won't get deallocated until the loop exits.
To guard against this, you can wrap the contents of your while loop in autoreleasepool(invoking:). This will cause each iteration of your loop to have its own autorelease pool that is drained each time.
It would look something like this:
/// Return next line, or nil on EOF.
func nextLine() -> String? {
precondition(fileHandle != nil, "Attempt to read from closed file")
var result: String? = nil
// Read data chunks from file until a line delimiter is found:
while !atEof, result == nil {
result = autoreleasepool {
if let range = buffer.range(of: delimData) {
// Convert complete line (excluding the delimiter) to a string:
let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding)
// Remove line (and the delimiter) from the buffer:
buffer.removeSubrange(0..<range.upperBound)
return line
}
let tmpData = fileHandle.readData(ofLength: chunkSize)
if tmpData.count > 0 {
buffer.append(tmpData)
} else {
// EOF or read error.
atEof = true
if buffer.count > 0 {
// Buffer contains last line in file (not terminated by delimiter).
let line = String(data: buffer as Data, encoding: encoding)
buffer.count = 0
return line
}
}
return nil
}
}
return result
}
As to whether your memory growth is a side effect of the debug environment, it's hard to say. But it would probably be wise to guard against this kind of growth regardless.

SocketScan Getting the Battery Level in Swift

Whatever I seem to try I cannot currently get back the Battery level from the iOS/SocketScan API. I am using version 10.3.36, here is my code so far:
func onDeviceArrival(result: SKTRESULT, device deviceInfo: DeviceInfo!) {
print("onDeviceArrival:\(deviceInfo.getName())")
scanApiHelper.postGetBattery(deviceInfo, target: self, response: #selector(onGetBatteryInfo))
}
func onGetBatteryInfo(scanObj: ISktScanObject) {
let result:SKTRESULT = scanObj.Msg().Result()
print("GetBatteryInfo status:\(result)")
if (result == ESKT_NOERROR) {
let batterylevel = scanObj.Property().getUlong()
print("Battery is:\(batterylevel)")
} else {
print("Error GetBatteryInfo status:\(result)")
}
However, the values I get back are:
GetBatteryInfo status:0
Battery is:1677741312
If my code is correct then how do I make the Battery result I get back a meaningful result, like a percentage? If I'm way off then how do I get back info like the battery level, firmware version etc?
Thanks
David
EDIT: SKTBATTERY_GETCURLEVEL isn't supported in Swift. However, the docs explain that the battery level response includes the min, current and max levels encoded in the first, second and third bytes, respectively.
The following is equivalent to using SKTBATTERY_GETCURLEVEL
Swift
func onGetBatteryInfo(scanObj: ISktScanObject) {
let result:SKTRESULT = scanObj.Msg().Result()
if(SKTSUCCESS(result)){
let batteryInfo = scanObj.Property().getUlong();
let batteryMin = ((batteryInfo >> 4) & 0xff);
let batteryCurrent = ((batteryInfo >> 8) & 0xff);
let batteryMax = ((batteryInfo >> 12) & 0xff);
let batteryPercentage = batteryCurrent / (batteryMax - batteryMin);
print("Battery is:\(batteryPercentage)")
self.setBatteryLevel = batteryPercentage
self.tableView.reloadData
} else {
print("Error GetBatteryInfo status:\(result)")
}
}
Objective-C
-(void) onGetBatteryInfo:(ISktScanObject*)scanObj {
SKTRESULT result=[[scanObj Msg]Result];
if(SKTSUCCESS(result)){
long batteryLevel = SKTBATTERY_GETCURLEVEL([[scanObj Property] getUlong]);
NSLog(#"BatteryInfo %ld", batteryLevel);
[self setBatteryLevel:batteryLevel];
[self.tableView reloadData];
} else {
NSLog(#"Error GetBatteryInfo status: %ld",result);
}
}
Here's code I use. Theres a variable defined in appDelegate for the batteryPercentage, and that is read when the v value is needed. The value is updated each 120 seconds by a timer, this way actions can occur as the level drops etc.
func onBatteryLevel (scanObj: ISktScanObject) {
let result: SKTRESULT = scanObj.Msg().Result()
if (SKTRESULT(result) > -1) {
let property: ISktScanProperty = scanObj.Property()
var batteryLevel = property.getUlong()
#if arch(x86_64) || arch(arm64)
batteryLevel = (batteryLevel<<(48))>>(56)
#else
batteryLevel = (batteryLevel<<(48-32))>>(56-32)
#endif
batteryPercentage = Int(batteryLevel)
} else {
debug ("data error \(result)")
}
}
For Swift 4 I just came across this problem and came up with the following solution.
var lastDeviceConnected : CaptureHelperDevice? {
didSet {
guard let lastDevice = self.lastDeviceConnected else { return }
lastDevice.getBatteryLevelWithCompletionHandler { result, batteryLevel in
guard result == SKTResult.E_NOERROR, let batteryLevel = batteryLevel else { return }
let minimum = SKTHelper.getMinimumLevel(fromBatteryLevel: Int(batteryLevel))
let maximum = SKTHelper.getMaximumLevel(fromBatteryLevel: Int(batteryLevel))
let current = SKTHelper.getCurrentLevel(fromBatteryLevel: Int(batteryLevel))
print("minimum: \(minimum)")
print("maximum: \(maximum)")
print("current: \(current)")
// current is out battery level in %
// minimum and maximum could for example be used for
// for a slider or something that visually presents
// the battery status
}
}
}
In my example I'm not handling the case that there could be no device or that the battery status might not have been retrieved as expected. I simply guard / return. In your example you might want to handle the issue.

Downloading files into root app directory using Swift

I'm new to coding for Swift and I don't have much experience of Objective C so am trying to download a file from the web (csv) and dump it into the root program directory.
Unfortunately I can't find any how-to tutorial in Swift although I am working through the tutorial at http://www.appcoda.com/background-transfer-service-ios7/ which is in ObjectiveC.
This may be really basic (and apologies) but I'm trying to create a class in Swift replacing the implementation of the FileDownloadInfo class in ObjectiveC. (If anyone has a Swift example of the tutorial, that would be REALLY helpful.
The implementation in ObjectiveC is:
#implementation FileDownloadInfo
-(id)initWithFileTitle:(NSString *)title andDownloadSource:(NSString *)source{
if (self == [super init]) {
self.fileTitle = title;
self.downloadSource = source;
self.downloadProgress = 0.0;
self.isDownloading = NO;
self.downloadComplete = NO;
self.taskIdentifier = -1;
}
return self;
}
#end
A FileDownloadArray is then populated via
-(void)initializeFileDownloadDataArray{
self.arrFileDownloadData = [[NSMutableArray alloc] init];
[self.arrFileDownloadData addObject:[[FileDownloadInfo alloc] initWithFileTitle:#"iOS Programming Guide" andDownloadSource:#"https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/iphoneappprogrammingguide.pdf"]];
}
I've created the following in a Swift class, but of course there's no function - how do I amend this to enable me to populate an array in the same way as above?
import UIKit
class FileDownloadInfo: NSObject {
var fileTitle: NSString
var downloadSource: NSString
var downloadTask: NSURLSessionDownloadTask?
var taskResumeData: NSData?
var downloadProgress: Double
var isDownloading: Bool
var downloadComplete: Bool
var taskIdentifier: Int
init(initWithFileTitle title: NSString, andDownloadSource source: NSString) {
self.fileTitle = title
self.downloadSource = source
self.downloadProgress = 0.0
self.isDownloading = false
self.downloadComplete = false
self.taskIdentifier = -1
}
}
I've only read part of the intro and this is my take on it. Hopefully, it can help. For the sake of direct translation, you can try:
func initializeFileDownloadDataArray(){
arrFileDownloadData = [FileDownloadInfo]()
arrFileDownloadData.append(FileDownloadInfo("iOS Programming Guide", downloadSource:"https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/iphoneappprogrammingguide.pdf" ))
}
If I read it correctly, the initializers would have a format like the one below:
init(title: String, downloadSource: String) {
self.fileTitle = title
self.downloadSource = source
self.downloadProgress = 0.0
self.isDownloading = false
self.downloadComplete = false
self.taskIdentifier = -1
}
Hopefully, that works.

Resources