1

N.B: This is not a repeat question, I have looked at the other solutions but none are working in Swift 3, but they do in Swift 2.

I need to get an image from a photo library with UIImagePickerController, that is working fine, then what I need to do, is to somehow save that to a public record in CloudKit, I am very open as to how this is done. Please, if possible, be clear as to what I need to change/add and where.

I have provided my entire view controller file just to be sure.

import UIKit
import CloudKit

var email: String = ""



class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {


    //MARK: Properties

    @IBOutlet weak var firstNameField: UITextField!
    @IBOutlet weak var lastNameField: UITextField!
    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    @IBOutlet weak var confirmPasswordField: UITextField!
    @IBOutlet weak var notMatching: UILabel!
    @IBOutlet weak var emailLabel: UILabel!
    @IBOutlet weak var errorLabel: UILabel!

    @IBOutlet weak var photoImageView: UIImageView!



    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.firstNameField.autocorrectionType = .no
        self.lastNameField.autocorrectionType = .no
        self.emailField.autocorrectionType = .no
        self.passwordField.autocorrectionType = .no
        self.confirmPasswordField.autocorrectionType = .no
        self.notMatching.isHidden = true

        firstNameField.delegate = self
        lastNameField.delegate = self
        emailField.delegate = self
        passwordField.delegate = self
        confirmPasswordField.delegate = self


    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //MARK: UIImagePickerControllerDelegate

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {

        // Dismiss the picker if the user canceled.
        dismiss(animated: true, completion: nil)

    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

        // The info dictionary may contain multiple representations of the image. You want to use the original.
        guard let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage else {
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
        }

        // Set photoImageView to display the selected image.
        photoImageView.image = selectedImage



        // Dismiss the picker.
        dismiss(animated: true, completion: nil)


    }


    //MARK: Actions

    @IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {

        //Hide keyboards
        firstNameField.resignFirstResponder()
        lastNameField.resignFirstResponder()
        emailField.resignFirstResponder()
        passwordField.resignFirstResponder()
        confirmPasswordField.resignFirstResponder()

        let imagePickerController = UIImagePickerController()

        imagePickerController.sourceType = .photoLibrary


        imagePickerController.delegate = self
        present(imagePickerController, animated: true, completion: nil)

    }



    @IBAction func signUpPressed(_ sender: UIButton) {

        let container = CKContainer.default()
        let pubDB = container.publicCloudDatabase
        //let privDB = container.privateCloudDatabase

        //Check if users exist
        let query = CKQuery(recordType: "MyUsers", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
        pubDB.perform(query, inZoneWith: nil, completionHandler: { (records, error) in

            //error code 11 is no objects found
            if error == nil || error?._code == 11 {

                var emailExists = false

                for record in records! {

                    if record.object(forKey: "email") as? String == self.emailField.text {
                        //other user with the same username exists - don't allow user to create account
                        emailExists = true

                    }

                }

                if emailExists == true {


                    self.emailLabel.text = "\(self.emailField.text!) is taken. Please choose another one."

                } else {

                    if self.firstNameField.text != nil && self.lastNameField.text != nil && self.passwordField.text == self.confirmPasswordField.text {

                        //user can sign up


                        let record = CKRecord(recordType: "MyUsers")
                        record.setObject(self.emailField.text! as CKRecordValue?, forKey: "email")
                        record.setObject(self.passwordField.text! as CKRecordValue?, forKey: "password")
                        record.setObject(self.firstNameField.text! as CKRecordValue?, forKey: "firstName")
                        record.setObject(self.lastNameField.text! as CKRecordValue?, forKey: "lastName")



                        print("all good")

                        pubDB.save(record, completionHandler: { (record, error) in

                            if error == nil {

                                OperationQueue.main.addOperation {

                                    UserDefaults.standard.set(self.emailField.text!, forKey: "Email")
                                    email = self.emailField.text!
                                    //self.performSegue(withIdentifier: "Games", sender: self)

                                }

                            } else {

                                print(error)

                            }

                        })


                    } else {



                    }


                }



            } else {

                print(error)

            }

        })

    }



    // MARK: UITextFieldDelegate

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // Hide the keyboard.
        textField.resignFirstResponder()
        return true
    }



}

Thank you!

David Buck
  • 3,439
  • 29
  • 24
  • 31
RufusV
  • 398
  • 2
  • 16
  • Please see my answer below. You also should know how to retrieve a `CKAsset` at some point. If you're going to be storing an asset you'll need to fetch it too. – Pierce Jan 13 '17 at 21:48

2 Answers2

8

Here is a simple way to save an image as a CKAsset with CloudKit. Please make sure to change the name for your Record, and the field name for the asset from when you set up the record.

let documentsDirectoryPath:NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
var imageURL: URL!
let tempImageName = "Image2.jpg"


func saveImage(_ image: UIImage?) {

    // Create a CKRecord
    let newRecord:CKRecord = CKRecord(recordType: "<INSERT_RECORD_NAME")

    if let image = image {

        let imageData:Data = UIImageJPEGRepresentation(image, 1.0)!
        let path:String = self.documentsDirectoryPath.appendingPathComponent(self.tempImageName)
        try? UIImageJPEGRepresentation(image, 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomic])
        self.imageURL = URL(fileURLWithPath: path)
        try? imageData.write(to: self.imageURL, options: [.atomic])

        let File:CKAsset?  = CKAsset(fileURL: URL(fileURLWithPath: path))
        newRecord.setObject(File, forKey: "<INSERT_RECORD_ASSET_FIELD_NAME")
    }

    if let database = self.publicDatabase {

        database.save(newRecord, completionHandler: { (record:CKRecord?, error:Error?) in

            // Check if there was an error
            if error != nil {
                NSLog((error?.localizedDescription)!)
            }
            else if let record = record {

                // Do whatever you want with the record, but image record was saved, asset should be saved.

            }


        })


    }


}

If you can't do JPEG format, and need to save as .png you can substitute the UIImageJPEGRepresentation section with this:

let imageData:Data = UIImagePNGRepresentation(image)!
try? UIImagePNGRepresentation(image)!.write(to: URL(fileURLWithPath: path), options: [.atomic])

And make the tempImageName something like let tempImageName = "Image2.png"

Hope this helps

Pierce
  • 2,973
  • 11
  • 31
  • Thank you so much, I've had two apps I couldn't do this on and have been stuck for almost 4 months, this is the first working solution! – RufusV Jan 14 '17 at 15:44
  • @RufusV - You're very welcome. Please feel free to ask me any more questions you may have. I have a lot of experience working with `CloudKit` – Pierce Jan 14 '17 at 16:24
  • Hi Pierce, I've got a question for CloudKit if you've got a moment. Here is the link: http://stackoverflow.com/questions/42370112/fetching-public-record-from-icloud-using-cloudkit Thank you! :) – Jack Robson Feb 21 '17 at 14:32
  • @JackRobson - I just checked it out, and it looks like you solved it! Good job – Pierce Feb 21 '17 at 18:25
  • Thank's for checking it out Pierce! Much appreciated :) – Jack Robson Feb 21 '17 at 18:48
0

Swift 5 version, but abstracted to a function where I can pass in a UI Image object and return the URL to be used. Useful where I had the image saved in CoreData and wanted to later upload to CloudKit as well.

func getImageURL(for image: UIImage?) -> URL {
    let documentsDirectoryPath:NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
    let tempImageName = "tempImage.jpg"
    var imageURL: URL?

    if let image = image {

        let imageData:Data = image.jpegData(compressionQuality: 1.0)!
        let path:String = documentsDirectoryPath.appendingPathComponent(tempImageName)
        try? image.jpegData(compressionQuality: 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomic])
        imageURL = URL(fileURLWithPath: path)
        try? imageData.write(to: imageURL!, options: [.atomic])
    }
    return imageURL!
}

And then how I use it:

  let imageURL = getImageURL(for: UIImage(data: itemToPublish!.Photo!)!)
  let imageAsset = CKAsset(fileURL: imageURL)
            itemBeingUpdated["photo"] = imageAsset  

  CKContainer.default().publicCloudDatabase.save(challengeRecord) { ...
Steve B
  • 413
  • 3
  • 11