Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I save an audio file to iCloud using Swift?

I have created an app using Swift 3 and Xcode 8.3.3 that records audio files and saves them to the app's Document directory. I would now like to save these files to iCloud to have them backed up. I have been able to save a simple record to iCloud using this code:

let database = CKContainer.default().publicCloudDatabase

func saveToCloud(myContent: String){
    let myRecord = CKRecord(recordType: "AudioRecording")
    myRecord.setValue(myContent, forKey: "content")
    database.save(myRecord) { (record, error) in
        print(error ??  "No error")
        guard record != nil else {return}
        print("Saved record to iCloud")
    }
}

It seems like I should just need to add a line of code that would look something like this:

newNote.setValue(audioObject, forKey: "Audio")

But I'm not sure what object I need to pass it for audioObject and if iCloud will be able to handle the object. Any suggestions?

like image 887
Ryan Tensmeyer Avatar asked Oct 24 '25 18:10

Ryan Tensmeyer


2 Answers

Using iOS 10.x Swift 3.0

You would save your audioObject as a blob of data; or in iCloud speak, an asset. Here is some basic code that saves an image, but it is the same principle, just a blob of data.

There is quite a bit more code here than you really need but I left it in to keep it all in context.

func files_saveImage(imageUUID2Save: String) {
    var localChanges:[CKRecord] = []
    let image2updated = sharedDataAccess.image2Cloud[imageUUID2Save]

    let newRecordID = CKRecordID(recordName: imageUUID2Save)
    let newRecord = CKRecord(recordType: "Image", recordID: newRecordID)

    let theLinkID = CKReference(recordID: sharedDataAccess.iCloudID, action: .deleteSelf)
    let thePath = sharedDataAccess.fnGet(index2seek: sharedDataAccess.currentSN)
    newRecord["theLink"] = theLinkID
    newRecord["theImageNo"] = image2updated?.imageI as CKRecordValue?
    newRecord["theImagePath"] = sharedDataAccess.fnGet(index2seek: image2updated?.imageS as! Int) as CKRecordValue?
    newRecord["theUUID"] = imageUUID2Save as CKRecordValue?

    let theURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(NSUUID().uuidString+".dat")
    do {
        try image2updated?.imageD.write(to: theURL!)
    } catch let e as NSError {
        print("Error! \(e)");
        return
    }

    newRecord["theImageBlob"] = CKAsset(fileURL:  URL(string: (theURL?.absoluteString)!)!)

    localChanges.append(newRecord)
    let records2Erase:[CKRecordID] = []

    let saveRecordsOperation = CKModifyRecordsOperation(recordsToSave: localChanges, recordIDsToDelete: records2Erase)
    saveRecordsOperation.savePolicy = .changedKeys
    saveRecordsOperation.perRecordCompletionBlock =  { record, error in
    if error != nil {
        print(error!.localizedDescription)
    }
    // deal with conflicts
    // set completionHandler of wrapper operation if it's the case
    }
    saveRecordsOperation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
        self.theApp.isNetworkActivityIndicatorVisible = false
        if error != nil {
            print(error!.localizedDescription, error!)
        } else {
            print("ok")
        }
    }

    saveRecordsOperation.qualityOfService = .background
    privateDB.add(saveRecordsOperation)
    theApp.isNetworkActivityIndicatorVisible = true
}

When you want to go the other way around, you decode your blob from iCloud with code like this snipit.

 let imageAsset = record["theImageBlob"] as? CKAsset
                if let _ = imageAsset {
                    if let data = NSData(contentsOf: (imageAsset?.fileURL)!) {
                        imageObject = data
                    }
                }

Obviously again this example is dealing with image data, but you and I know its all data :) no mater what colour it is.

The only cavet here is speed, I am pretty sure assets are kept in a different forest to your run-of-the-mill iCloud objects, and accessing them can be a tad slower.

like image 161
user3069232 Avatar answered Oct 26 '25 06:10

user3069232


This is the same exact process if you wanted to save/read a video file

Here is how to write an audio file. Save it as a CKAsset:

func save(audioURL: URL) {

    let record = CKRecord(recordType: "YourType")

    let fileURL = URL(fileURLWithPath: audioURL.path)

    let asset = CKAsset(fileURL: fileURL)

    record["audioAsset"] = asset

    CKContainer.default().publicCloudDatabase.save(record) { (record, err) in
        if let err = err {
            print(err.localizedDescription)
            return 
        }
        if let record = record { return }
        print("saved: ", record.recordID)
    }
}

Here is how to read the audio file from a CKAsset:

func fetchAudioAsset(with recordID: CKRecord.ID) {

    CKContainer.default().publicCloudDatabase.fetch(withRecordID: recordID) { 
        [weak self](record, err) in

        DispatchQueue.main.async {

            if let err = err {
                print(err.localizedDescription)
                return 
            }

            if let record = record { return }

            guard let audioAsset = record["audioAsset"] as? CKAsset else { return }

            guard let audioURL = audioAsset.fileURL else { return }

            do {
        
                self?.audioPlayer = try AVAudioPlayer(contentsOf: audioURL)

            } catch {
                print(error.localizedDescription)
            }
        }
    }
}
like image 36
Lance Samaria Avatar answered Oct 26 '25 08:10

Lance Samaria



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!