How does Any, Optional and Protocol work together in Swift? - ios

Can somebody explain why the first snippet is failing and the second is working?
Error is optional type
var error:Error? = NSError(domain: "MyDomain", code: 1001, userInfo: nil)
var anyError:Any = error
if let err = anyError as? Error
{
print("success")
}
else {
print("failure") // prints failure
}
NSError is optional type
var error:NSError? = NSError(domain: "MyDomain", code: 1001, userInfo: nil)
var anyError:Any = error
if let err = anyError as? NSError
{
print("success") // prints success
}
else {
print("failure")
}

Related

Firebase / Firestore not getting document and running code

I am getting a document from firebase in swift. However the line isn't being run and is not getting the data.
This is my code:
let db = Firestore.firestore()
db.collection("chats").document(userDefaults.string(forKey: "currentGroup")!).collection("messages").document("variable").getDocument { (document, error) in
if error != nil{
print("error getting document")
}
else{
let documentData = document!.data()
let startNumOfMessages = documentData!["numOfMessages"] as! Int
var messageArray: Array<String> = []
if startNumOfMessages > 0 {
for message in 1...startNumOfMessages{
print(message)
//THIS LINE ISNT RUNNING
db.collection("chats").document(self.userDefaults.string(forKey: "currentGroup")!).collection("messages").document("\(message)").getDocument { (messageDoc, err) in
if err != nil{
print("Error getting message \(message)")
}
else{
if messageDoc!.exists && messageDoc != nil{
let messData = messageDoc!.data()
print(messData!["message"]!)
messageArray.append(messData!["message"] as! String)
}
else{
print("error in document")
}
}
}
}
//Display them
for num in 0...messageArray.count{
let label = UILabel()
label.text = messageArray[num]
self.stackView.addArrangedSubview(label)
}
}
}
}
The line below the comment is the line that isn't running. And the line that says label.text = messageArray[num] displays an error
Fatal error: Index out of range
Showing it doesn't get the data.
You miss the asynchronous way use DispatchGroup ( numbered from 1 to 4 )
let db = Firestore.firestore()
db.collection("chats").document(userDefaults.string(forKey: "currentGroup")!).collection("messages").document("variable").getDocument { (document, error) in
if error != nil{
print("error getting document")
}
else{
let documentData = document!.data()
let startNumOfMessages = documentData!["numOfMessages"] as! Int
var messageArray: Array<String> = []
if startNumOfMessages > 0 {
let g = DispatchGroup() /// 1
for message in 1...startNumOfMessages{
print(message)
//THIS LINE ISNT RUNNING
g.enter() /// 2
db.collection("chats").document(self.userDefaults.string(forKey: "currentGroup")!).collection("messages").document("\(message)").getDocument { (messageDoc, err) in
if err != nil{
print("Error getting message \(message)")
}
else{
if messageDoc!.exists && messageDoc != nil{
let messData = messageDoc!.data()
print(messData!["message"]!)
messageArray.append(messData!["message"] as! String)
}
else{
print("error in document")
}
}
g.leave() /// 3
}
}
g.notify(queue: .main) { /// 4
//Display them
for num in 0...messageArray.count{
let label = UILabel()
label.text = messageArray[num]
self.stackView.addArrangedSubview(label)
}
}
}
}
}

SwiftUI Scrollview Kingfisher KFImage Firebase Storage IDEDebugSessionErrorDomain Code: 4 Message from debugger: Terminated due to memory issue

I'm developing in XCode 12.3 using Swift/SwiftUI. I'm having an issue where, when calling the Firebase storage downloadAPI, Image memory is not freed from the scrollView as the user scrolls. Eventually the app crashes ~3GB of memory. If I call my Flickr Image download, the memory is freed just fine and maintains memory at about 140MB.
My specific question is, what do I need to change to allow the firebase call to drop images from memory when scrolling, similar to how the Flickr call works?
I receive the following Errors along the way when I call Firebase, but the images are retrieved fro the follownig url passed to the firebase API gs://<MY DOMAIN>/figure_images/SWBS6I_100300_PrimaryFrontImage_Small.jpg
2021-01-02 11:41:50.776056-0500 xxx[xxx] Task <xxx>.<87> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=SWBS6I_116300_PrimaryFrontImage_Small.jpg, NSErrorFailingURLKey=SWBS6I_116300_PrimaryFrontImage_Small.jpg, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <xxx>.<87>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <xxx>.<87>, NSUnderlyingError=0x2800c0840 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
Error on crashing
iPhone quit unexpectedly.
Domain: IDEDebugSessionErrorDomain
Code: 4
Failure Reason: Message from debugger: Terminated due to memory issue
--
System Information
macOS Version 11.1 (Build 20C69)
Xcode 12.3 (17715) (Build 12C33)
Timestamp: 2021-01-02T11:44:56-05:00
SwiftUI Views
import SwiftUI
import struct Kingfisher.KFImage
struct SeriesView: View {
// Firebase auth
#EnvironmentObject var authState: AuthenticationState
// Listen for view model state changes, i.e. series
#ObservedObject var seriesList = seriesListSharedData
var showFigures: [Figure] {
let showFiguresFilter: [Figure] = seriesList.figureArray.filter({$0.seriesUniqueId == SeriesUniqueIdEnum.SWBS6I})
return showFiguresFilter
}
// view body
var body: some View {
ScrollView {
LazyVGrid(columns: [
GridItem(.adaptive(minimum: 100, maximum: 100))
], content: {
ForEach(showFigures, id: \.self) { figure in
FigurePhoto(figure: figure)
}
})
}
}
}
struct FigurePhoto: View {
#ObservedObject var figure: Figure
var body: some View {
// name photo and specifics
VStack(alignment: .center, spacing: 4) {
// image and specifics
HStack(alignment: .top, spacing: 4) {
VStack(alignment: .center, spacing: 0) {
let image = figure.imagePrimaryFrontString ?? ""
KFImage(URL(string: image))
.resizable()
.aspectRatio(1, contentMode: .fill)
Spacer()
} // end vstack
.onAppear() {
// (1) WORKS! - Call to flickr function works fine ~ 140MB
figure.fetchFlickrImageURL(withTags: figure.figureGlobalUniqueId, withText: "\(figure.figureGlobalUniqueId)\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix)")
// One call OR the other, not both
// (2) MEMORY ISSUE - Call to firebase function retains memory of every photo whether in view or not until the app crashes ~3GB
figure.fetchFirebasePrimaryFrontImageString(firebaseStorageRef: kFigureImageRoot)
} // end vstack on appear
} // end hstack
} // end vstack
} // end body
}
Flickr Function that manages memory as anticipated, releasing memory when scrolling
// fetch all flickr photo images - set the primary photo as the first photo
func fetchFlickrImageURL(withTags tags: String, withText text: String?) {
var stringURL = String()
let flickr = Flickr()
flickr.flickrPhotosSearch(withTags: tags, withText: text) {
results, error in
if let error = error {
print("Error searching Flickr: \(error)")
}
if let results = results {
// check for no photos
if results.searchResults.count == 0 {
return
}
for flickrPhoto in results.searchResults {
// set front image if text is front image
if let text = text {
// insert the primary image at the beginning
if let farm = flickrPhoto.farm, let server = flickrPhoto.server, let secret = flickrPhoto.secret {
if text.contains(("\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix)")) {
stringURL = "https://farm\(farm).staticflickr.com/\(server)/\(flickrPhoto.photoID)_\(secret).jpg"
}
else {
stringURL = "https://farm\(farm).staticflickr.com/\(server)/\(flickrPhoto.photoID)_\(secret).jpg"
}
}
}
}
self.imagePrimaryFrontString = stringURL
}
}
}
Firebase function that doesn't release memory when scrolling
func fetchFirebasePrimaryFrontImageString(firebaseStorageRef: String) {
let storage = Storage.storage()
// set the image locataion
getFirebasePrimaryFrontImageString()
// set the image
let storageRefImage = storage.reference().child(firebaseStorageRef)
let seriesImageRef = storageRefImage.child(self.imagePrimaryFrontString!)
seriesImageRef.downloadURL { firebaseImageURL, error in
if let error = error {
#if DEBUG
print("ERROR: Failure getting Firebase Series image: \(error.localizedDescription)")
#endif
}
else
{
self.imagePrimaryFrontString = firebaseImageURL!.absoluteString
}
}
}
func getFirebasePrimaryFrontImageString() {
self.imagePrimaryFrontString = "\(self.figureGlobalUniqueId)\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix).\(kImageJpgExt)"
}
Flickr actual call
func flickrPhotosSearch(withTags tags: String, withText text: String? = nil,
completion : #escaping (_ results: FlickrSearchResults?, _ error : NSError?) -> Void) {
var searchURL: URL
// check for name passed for front image
if let text = text {
guard let searchURLText = flickrSearch(withText: text) else {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
completion(nil, APIError)
return
}
searchURL = searchURLText
} else {
guard let searchURLTags = flickrSearch(withTags: tags) else {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
completion(nil, APIError)
return
}
searchURL = searchURLTags
}
let searchRequest = URLRequest(url: searchURL)
URLSession.shared.dataTask(with: searchRequest, completionHandler: { (data, response, error) in
if let _ = error {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil, APIError)
})
return
}
guard let _ = response as? HTTPURLResponse,
let data = data else {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil, APIError)
})
return
}
do {
guard let resultsDictionary = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) as? [String: AnyObject],
let stat = resultsDictionary["stat"] as? String else {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil, APIError)
})
return
}
switch (stat) {
case "ok":
break
case "fail":
if let message = resultsDictionary["message"] {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:message])
OperationQueue.main.addOperation({
completion(nil, APIError)
})
}
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: nil)
OperationQueue.main.addOperation({
completion(nil, APIError)
})
return
default:
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil, APIError)
})
return
}
guard let photosContainer = resultsDictionary["photos"] as? [String: AnyObject], let photosReceived = photosContainer["photo"] as? [[String: AnyObject]] else {
let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil, APIError)
})
return
}
var flickrPhotos = [FlickrPhoto]()
for photoObject in photosReceived {
guard let photoID = photoObject["id"] as? String,
let farm = photoObject["farm"] as? Int ,
let server = photoObject["server"] as? String ,
let secret = photoObject["secret"] as? String,
let originalSecret = photoObject["originalsecret"] as? String else {
break
}
let flickrPhoto = FlickrPhoto(photoID: photoID, farm: farm, server: server, secret: secret, originalSecret: originalSecret)
flickrPhotos.append(flickrPhoto)
// thumbnail image
guard let url = flickrPhoto.flickrImageURL(),
let imageData = try? Data(contentsOf: url as URL) else {
break
}
if let image = UIImage(data: imageData) {
flickrPhoto.thumbnail = image
}
}
OperationQueue.main.addOperation({
completion(FlickrSearchResults(searchTerm: tags, searchResults: flickrPhotos), nil)
})
} catch _ {
completion(nil, nil)
return
}
}) .resume()
}
Firebase POD signature and comments from FIRStorageReference #see https://cloud.google.com/storage/
/**
* Asynchronously retrieves a long lived download URL with a revokable token.
* This can be used to share the file with others, but can be revoked by a developer
* in the Firebase Console if desired.
* #param completion A completion block that either returns the URL on success,
* or an error on failure.
*/
open func downloadURL(completion: #escaping (URL?, Error?) -> Void)
Which upon debugging takes me here:
- (void)downloadURLWithCompletion:(FIRStorageVoidURLError)completion {
FIRStorageGetDownloadURLTask *task =
[[FIRStorageGetDownloadURLTask alloc] initWithReference:self
fetcherService:_storage.fetcherServiceForApp
dispatchQueue:_storage.dispatchQueue
completion:completion];
[task enqueue];
}

Invalid call to setPhotoURL: after commitChangesWithCallback

I am trying to implement a user profile view, in which the user chooses a profile image from camera roll and a username. When everything is chosen, the user clicks on a createProfileButton that calls the following function:
#objc func createProfileButtonPressed() {
let user = Auth.auth().currentUser
let uid = Auth.auth().currentUser?.uid
let firebaseStorageRef = Storage.storage().reference().child("profile_picture").child(uid!)
let database = Firestore.firestore()
// settings for disabling warning concerning firestore
let settings = database.settings
settings.areTimestampsInSnapshotsEnabled = true
database.settings = settings
let profileChangeRequest = user?.createProfileChangeRequest()
profileChangeRequest?.displayName = self.usernameTextfield.text
if let profileImage = profilePicture, let imageData = UIImageJPEGRepresentation(profileImage, 0.1) {
firebaseStorageRef.putData(imageData, metadata: nil) { (metadata, error) in
if error != nil {
print("Error:", error!)
return
}
firebaseStorageRef.downloadURL(completion: { (url, error) in
if error != nil {
print("Error:", error!)
return
}
print("Picture URL:", url!)
profileChangeRequest?.photoURL = url
})
}
}
profileChangeRequest?.commitChanges(completion: { (error) in
if error != nil {
print("Error:", error!)
return
}
print("Profile created")
var databaseRef = database.collection("users").addDocument(data: ["uid": uid!, "email": user?.email!, "username": user?.displayName, "profileImageURL": user?.photoURL?.absoluteString], completion: { (error) in
if error != nil {
print("Error storing user", error!)
return
}
print("user successfully stored!")
})
self.navigationController?.popToRootViewController(animated: true)
})
}
I want the users email, username, uid and photoURL stored in a firestore document. However, what I get is this error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid call to setPhotoURL: after commitChangesWithCallback:.'
This happens after the popToRootViewController function gets called. Why does this error message show up although I want to set the photoURL before I commit the changes?
Just to expand on ekscrypto's comment. Your call to profileChangeRequest?.photoURL = url is taking place within the download callback, thus it going to be run after your call to commitChanges.
You need to put your commitChanges block inside your callback -
if let profileImage = profilePicture, let imageData = UIImageJPEGRepresentation(profileImage, 0.1) {
firebaseStorageRef.putData(imageData, metadata: nil) { (metadata, error) in
if error != nil {
print("Error:", error!)
return
}
firebaseStorageRef.downloadURL(completion: { (url, error) in
if error != nil {
print("Error:", error!)
return
}
print("Picture URL:", url!)
profileChangeRequest?.photoURL = url
profileChangeRequest?.commitChanges(completion: { (error) in
if error != nil {
print("Error:", error!)
return
}
print("Profile created")
var databaseRef = database.collection("users").addDocument(data: ["uid": uid!, "email": user?.email!, "username": user?.displayName, "profileImageURL": user?.photoURL?.absoluteString], completion: { (error) in
if error != nil {
print("Error storing user", error!)
return
}
print("user successfully stored!")
})
self.navigationController?.popToRootViewController(animated: true)
})
})
}
}

Add try catch and convert a function to a generic class swift

In one of my view controllers I have a function to establish a tcp connection , send and receive messages which is as follows.
func tcpConnection(host: NSString){
var client = TCPClient(addr: String(host) as String, port: Config.tcpPort)
let (success, err) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(success)")
guard success else { log?.error("Cannot Connect \(err)"); return}
let (successMsg, failmsg) = client.send(str: self.jsonString)
let data = client.read(Config.expectedByteLength)
guard let d = data else { return }
guard let recievedMsg = String(bytes: d, encoding: NSUTF8StringEncoding) else { return }
self.recivedMsgFromServer = recievedMsg
log?.debug("Recieved msg\(recievedMsg)")
let (clientClosed, errormsg) = client.close()
guard clientClosed else { return }
}
I am using this piece of code twice in the same view controller. So I want to have a generic class for this function . Also I have many guards which I want to replace with a single try catch block.
Also after receiving a message I am doing different things in both the tcp connection functions.
Following is what I have tried until now.
class TcpService{
var jsonString : String = ""
func tcpConnection(host: NSString){
do {
var client = try TCPClient(addr: String(host) as String, port: Config.tcpPort)
let (success, err) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(success)")
guard success else { log?.error("Cannot Connect \(err)"); return}
let (successMsg, failmsg) = client.send(str: self.jsonString)
let data = client.read(Config.expectedByteLength)
guard let d = data else { return }
guard let recievedMsg = String(bytes: d, encoding: NSUTF8StringEncoding) else { return }
log?.debug("Recieved msg\(recievedMsg)")
/*Do Something different in every viewController
//For Example
self.Info = Mapper<Info>().map(recievedMsg)
log?.debug("Status\(self.Info?.Status)")
*/
let (clientClosed, errormsg) = client.close()
guard clientClosed else { return }
} catch {
let fetchError = error as NSError
print(fetchError)
// Handle Error
}
}
}
When I try to call this function from the new class in my view controller it does not behave in the same way the function in the view controller did.
Is there anything I should change in the way I have created the class?
Any help will be appreciated as I am very new to swift. Thank you
This is a way your problem could be solved. Skip the custom error throwing part if you don't actually need it. The way to refactor it is using callback function, just send your function into this one, i.e.:
TCPService.connect("some_host", message: "your_message") { response in
Mapper<Info>().map(response)
}
Code:
import Foundation
extension NSError {
static func error(with localized: String) -> NSError {
return NSError(domain: "error", code: 2, userInfo: [NSLocalizedDescriptionKey: localized])
}
}
class TCPService {
static func connect(host: String, andSend message: String, onComplete: String -> ()) {
do {
let client = try TCPClient(addr: host, port: Config.tcpPort)
let (successfulConnection, error) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(successfulConnection)")
guard successfulConnection else {
throw NSError.error(with: error)
log?.error("Cannot Connect \(error)")
return
}
let (successfullySent, failureMessage) = client.send(str: message)
let data = client.read(Config.expectedByteLength)
guard successfullySent,
let d = data,
receivedMessage = String(bytes: d, encoding: NSUTF8StringEncoding) else {
throw NSError.error(with: failureMessage)
return
}
log?.debug("Received msg\(receivedMessage)")
onComplete(receivedMessage)
let (clientClosed, errorMessage) = client.close()
guard clientClosed else {
throw NSError.error(with: errorMessage)
return
}
} catch let error as NSError {
let fetchError = error as NSError
print(fetchError)
// Handle errors
}
}
}

nil while unwrapping an optinal value SWIFT [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
I have downloaded a project from https://github.com/doberman/speaker-gender-detect--ios. I followed the instructions but when I run the app, I get a message that says: fatal error: unexpectedly found nil while unwrapping an Optional value. How can I fix the crash so that the app works.
The app crashes here:
let genderEqualityRatios = self.calcGenderEquality (String (response.result.value!))
This is my code:
import AVFoundation
import Alamofire
import SwiftyJSON
protocol AudioRecorderDelegate {
func audioRecorder(audioRecorder: AudioRecorder?, updatedLevel: Float)
func audioRecorder(audioRecorder: AudioRecorder?, updatedGenderEqualityRatio: (male: Float, female: Float))
}
class AudioRecorder: NSObject {
static let sharedInstance: AudioRecorder = AudioRecorder()
private let kRemoteURL: NSURL = NSURL(string: "xxx.xxx.xxx.xxx")! // change to your API endpoint URL
private let kPostAudioInterval: NSTimeInterval = 10.0 // change to post to API more/less frequently
var delegate: AudioRecorderDelegate?
private let recorderSettings = [
AVSampleRateKey: NSNumber(float: Float(16000.0)),
AVFormatIDKey: NSNumber(int: Int32(kAudioFormatMPEG4AAC)),
AVNumberOfChannelsKey: NSNumber(int: 1),
AVEncoderAudioQualityKey: NSNumber(int: Int32(AVAudioQuality.High.rawValue))
]
private var recorder: AVAudioRecorder?
private var checkLevelsTimer: NSTimer?
private var postTimer: NSTimer?
private var maleDuration: Float = 0.0
private var femaleDuration: Float = 0.0
override init() {
super.init()
do {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(AVAudioSessionCategoryRecord)
} catch let err as NSError {
print("Failed to initialize AudioRecorder: \(err)")
}
}
func startRecording() {
// print("startRecording")
if self.recorder != nil && self.recorder!.recording {
self.stopRecording()
}
let audioURL: NSURL = self.getAudioURL()
// print("got audioURL: '\(audioURL)'")
do {
self.recorder = try AVAudioRecorder(URL: audioURL, settings: self.recorderSettings)
self.recorder?.meteringEnabled = true
self.recorder?.prepareToRecord()
} catch let err as NSError {
print("Failed to set up AVAudioRecorder instance: \(err)")
}
guard self.recorder != nil else { return }
self.recorder?.record()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
self.checkLevelsTimer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: "checkLevels", userInfo: nil, repeats: true)
self.postTimer = NSTimer.scheduledTimerWithTimeInterval(kPostAudioInterval, target: self, selector: "onPostTimerTrigger", userInfo: nil, repeats: true)
} catch let err as NSError {
print("Failed to activate audio session (or failed to set up checkLevels timer): \(err)")
}
}
func stopRecording(shouldSubmitAudioAfterStop: Bool = false) {
// print("stopRecording")
guard self.recorder != nil else {
print("`self.recorder` is `nil` - no recording to stop")
return
}
self.recorder?.stop()
if let t = self.checkLevelsTimer {
t.invalidate()
self.checkLevelsTimer = nil
}
if let t = self.postTimer {
t.invalidate()
self.postTimer = nil
}
let audioURL: NSURL = self.recorder!.url
self.recorder = nil
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(false)
if shouldSubmitAudioAfterStop {
self.postAudio(audioURL)
} else {
// print("`shouldSubmitAudioAfterStop` is `false` - I won't post audio")
}
} catch let err as NSError {
print("Failed to deactivate audio session (or failed to post audio): \(err)")
}
}
// MARK: -
func checkLevels() {
guard self.recorder != nil else {
print("`self.recorder` is `nil` - can't check levels")
return
}
self.recorder?.updateMeters()
let averagePower: Float = self.recorder!.averagePowerForChannel(0)
if let d = self.delegate {
d.audioRecorder(self, updatedLevel: averagePower)
} else {
print("AudioRecorder - averagePower: \(averagePower)")
}
}
func onPostTimerTrigger() {
// print("onPostTimerTrigger")
guard let r = self.recorder else {
print("`self.recorder` is `nil` - no audio to post")
return
}
if !r.recording {
print("not recording - no audio to post")
}
self.stopRecording(true)
self.startRecording()
}
// MARK: -
private func getAudioURL(filename: String = "recording") -> NSURL {
let fileManager: NSFileManager = NSFileManager.defaultManager()
let urls: [NSURL] = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let documentDirectory: NSURL = urls[0] as NSURL
let uniqueFilename = "\(filename)_\(NSDate().timeIntervalSince1970)"
let audioURL: NSURL = documentDirectory.URLByAppendingPathComponent("\(uniqueFilename).m4a")
return audioURL
}
private func postAudio(audioURL: NSURL) {
// print("AudioRecorder.postAudio - audioURL: \(audioURL.absoluteString)")
Alamofire.upload(Method.POST, kRemoteURL, multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(fileURL: audioURL, name: "file")
}, encodingCompletion: { encodingResult in
switch encodingResult {
case .Success (let upload, _, _):
upload.responseString { response in
//print("response: \(response)")
let genderEqualityRatios = self.calcGenderEquality(String(response.result.value!))
if let eq = genderEqualityRatios, let d = self.delegate {
d.audioRecorder(self, updatedGenderEqualityRatio: eq)
}
}
case .Failure(let encodingError):
print("encodingError: \(encodingError)")
}
})
}
private func calcGenderEquality(response: String) -> (male: Float, female: Float)? {
guard let dataFromString = response.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) else {
return nil
}
let json = JSON(data: dataFromString)
for selection in json["selections"].arrayValue {
if selection["gender"] == "M" {
self.maleDuration = self.maleDuration + (selection["endTime"].floatValue - selection["startTime"].floatValue)
} else if selection["gender"] == "F" {
self.femaleDuration = self.maleDuration + (selection["endTime"].floatValue - selection["startTime"].floatValue)
}
}
let spokenDuration = self.maleDuration + self.femaleDuration
let maleFactor = self.maleDuration / spokenDuration
let femaleFactor = self.femaleDuration / spokenDuration
guard !maleFactor.isNaN else {
print("Failed to calculate gender equality (`maleFactor` is `NaN`)")
return nil
}
guard !femaleFactor.isNaN else {
print("Failed to calculate gender equality (`femaleFactor` is `NaN`)")
return nil
}
return (male: maleFactor, female: femaleFactor)
}
}
Try it like this:
if let genderEqualityRatios = response.result.value as? String {
self.calcGenderEquality(genderEqualityRatios)
} else {
print("a problem occurred and we couldn't call calcGenderEquality")
}

Resources