photoLibrary에 접근하여 사진을 불러오고 사진의 이름과 간단한 설명을 적고 cloudKit 에 저장한다. 그리고 클라우드에 저장된 내용을 불러오고, 수정하고, 삭제 할 수 있는 예제를 만들어 본다.

추가로 다른 기기에서 cloudkit에 새로운 사진을 저장했을때 단말에 push notificaton 이 오는 subscription 도 함께 만들어본다.


stroybord 에 textfield, textView, imageView 그리고 사진호출, 저장, 불러오기, 수정, 삭제 를 수행하는 5개의 버튼 을 만들고 controller에 연결하자.

프로젝트의 capabilities 탭에서 아이클라우드를 On 으로 하고 CloudKit 사용을 체크하는 것도 잊지 말자.





아래는 viewController.swift 파일의 전체 내용이다.


import UIKit

import CloudKit

import MobileCoreServices


class ViewController: UIViewController , UIImagePickerControllerDelegate, UINavigationControllerDelegate{

    

    let container = CKContainer.defaultContainer()

    var publicDatabase : CKDatabase?

    var currentRecord : CKRecord?

    var photoURL : NSURL?

    

    @IBOutlet var addressField: UITextField!

    

    @IBOutlet var commentsField: UITextView!

    

    @IBOutlet var imageView: UIImageView!

    


    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        publicDatabase = container.publicCloudDatabase

        

        let predicate = NSPredicate(format: "TRUEPREDICATE")

        let subscription = CKSubscription(recordType: "Houses", predicate: predicate, options: .FiresOnRecordCreation)

        let notificationInfo = CKNotificationInfo()

        notificationInfo.alertBody = "A new houses was added"

        notificationInfo.shouldBadge = true

        subscription.notificationInfo = notificationInfo

        publicDatabase?.saveSubscription(subscription, completionHandler: ({returnRecord , error in

            if let err = error {

                print("subscription failed %@", err.localizedDescription)

            } else {

                dispatch_async(dispatch_get_main_queue()) {

                    self.notifyUser("success", message: "subscription set up successfully")

                }

            }

        }))

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        addressField.endEditing(true)

        commentsField.endEditing(true)

    }

    

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

        self.dismissViewControllerAnimated(true, completion: nil)

        let image = info[UIImagePickerControllerOriginalImage] as! UIImage

        imageView.image = image

        photoURL = saveImageToFile(image)

    }

    func imagePickerControllerDidCancel(picker: UIImagePickerController) {

        self.dismissViewControllerAnimated(true, completion: nil)

    }

    func saveImageToFile(image : UIImage) -> NSURL {

        let filemgr = NSFileManager.defaultManager()

        let dirPaths = filemgr.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)

        let filePath = dirPaths[0].URLByAppendingPathComponent("currentImage.png").path

        UIImageJPEGRepresentation(image, 0.5)!.writeToFile(filePath!, atomically : true)

        return NSURL.fileURLWithPath(filePath!)

    }

    

    func notifyUser(title : String, message : String) -> Void {

        let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)

        let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)

        alert.addAction(cancelAction)

        self.presentViewController(alert, animated: true, completion: nil)

    }

    

    func fetchRecord(recordID : CKRecordID) -> Void {

        publicDatabase = container.publicCloudDatabase

        publicDatabase?.fetchRecordWithID(recordID, completionHandler: ({record, error in

            if let err = error {

                dispatch_async(dispatch_get_main_queue()) {

                    self.notifyUser("fetch errer", message: err.localizedDescription)

                }

            } else {

                dispatch_async(dispatch_get_main_queue()) {

                    self.currentRecord = record

                    self.addressField.text = record!.objectForKey("address") as? String

                    self.commentsField.text = record!.objectForKey("comment") as? String

                    let photo = record!.objectForKey("photo") as! CKAsset

                    let image = UIImage(contentsOfFile: photo.fileURL.path!)

                    self.imageView.image = image

                    self.photoURL = self.saveImageToFile(image!)

                }

            }

        }))

    }


    @IBAction func saveRecord(sender: AnyObject) {

        if(photoURL == nil) {

            notifyUser("No Photo",message: "use the photo option to choose a photo for the record")

            return

        }

        let asset = CKAsset(fileURL: photoURL!)

        let myRecord = CKRecord(recordType: "Houses")

        myRecord.setObject(addressField.text, forKey: "address")

        myRecord.setObject(commentsField.text, forKey: "comment")

        myRecord.setObject(asset, forKey: "photo")

        

        publicDatabase!.saveRecord(myRecord, completionHandler: ({returnRecord, error in

            if let err = error {

                self.notifyUser("Save Error",message: err.localizedDescription)

            } else {

                dispatch_async(dispatch_get_main_queue()) {

                    self.notifyUser("Success", message : "Record saved successfully")

                }

                self.currentRecord = myRecord

            }

        }))

    }

    

    @IBAction func performQuery(sender: AnyObject) {

        let predicate = NSPredicate(format: "address = %@", addressField.text!)

        let query = CKQuery(recordType: "Houses", predicate: predicate)

        publicDatabase?.performQuery(query, inZoneWithID: nil, completionHandler: ({results, error in

            if(error != nil) {

                dispatch_async(dispatch_get_main_queue()){

                    self.notifyUser("Cloud Access Error", message: error!.localizedDescription)

                }

            } else {

                if results!.count > 0 {

                    let record = results![0]

                    self.currentRecord = record

                    dispatch_async(dispatch_get_main_queue()) {

                        self.commentsField.text = record.objectForKey("comment") as! String

                        let photo = record.objectForKey("photo") as! CKAsset

                        let image = UIImage(contentsOfFile: photo.fileURL.path!)

                        self.imageView.image = image

                        self.photoURL = self.saveImageToFile(image!)

                        

                    }

                }else {

                    dispatch_async(dispatch_get_main_queue()) {

                        self.notifyUser("No match found", message: "no record matching the address was found")

                    }

                }

            }

        }))

    }

    

    @IBAction func selectPhoto(sender: AnyObject) {

        let imagePicker = UIImagePickerController()

        imagePicker.delegate = self

        imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary

        imagePicker.mediaTypes = [kUTTypeImage as String]

        

        self.presentViewController(imagePicker, animated: true, completion: nil)

    }

    

    @IBAction func updateRecord(sender: AnyObject) {

        if let record = currentRecord {

            let asset = CKAsset(fileURL: photoURL!)

            record.setObject(addressField.text, forKey: "address")

            record.setObject(commentsField.text, forKey: "comment")

            record.setObject(asset, forKey: "photo")

            

            publicDatabase!.saveRecord(record, completionHandler: ({returnRecord, error in

                if let err = error {

                    dispatch_async(dispatch_get_main_queue()) {

                        self.notifyUser("Update Error", message: err.localizedDescription)

                    }

                }else {

                    dispatch_async(dispatch_get_main_queue()) {

                        self.notifyUser("success", message: "record updated successfully")

                    }

                }

            }))

        }else {

            notifyUser("no record selected", message: "use query to select a record to update")

        }

    }

    

    @IBAction func deleteRecord(sender: AnyObject) {

        if let record = currentRecord {

            publicDatabase?.deleteRecordWithID(record.recordID, completionHandler: ({returnRecord, error in

                if let err = error {

                    dispatch_async(dispatch_get_main_queue()) {

                        self.notifyUser("delete error", message: err.localizedDescription)

                    }

                } else {

                    dispatch_async(dispatch_get_main_queue()) {

                        self.notifyUser("success", message: "record deleted successfully")

                    }

                }

            }))

        } else {

            notifyUser("no record selected", message: "use query to select a record to delete")

        }

    }







아래는 AppDelegate.swift 파일의 전체 소스이다.


import UIKit

import CloudKit


@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {


    var window: UIWindow?



    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        // Override point for customization after application launch.

        let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)

        application.registerUserNotificationSettings(settings)

        application.registerForRemoteNotifications()

        

        if let options : NSDictionary = launchOptions {

            let remoteNotification = options.objectForKey(UIApplicationLaunchOptionsRemoteNotificationKey) as? NSDictionary

            if let notification = remoteNotification {

                self.application(application, didReceiveRemoteNotification: notification as [NSObject : AnyObject])

            }

        }

        

        return true

    }

    

    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {

        let viewController : ViewController = self.window?.rootViewController as! ViewController

        let notification : CKNotification = CKNotification(fromRemoteNotificationDictionary : userInfo as! [String : NSObject])

        if(notification.notificationType == CKNotificationType.Query) {

            let queryNotification = notification as! CKQueryNotification

            let recordID = queryNotification.recordID

            viewController.fetchRecord(recordID!)

        }

    }


    func applicationWillResignActive(application: UIApplication) {

        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

    }


    func applicationDidEnterBackground(application: UIApplication) {

        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.

        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    }


    func applicationWillEnterForeground(application: UIApplication) {

        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.

    }


    func applicationDidBecomeActive(application: UIApplication) {

        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    }


    func applicationWillTerminate(application: UIApplication) {

        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

    }



}








위의 
코드는  "핵심만 골라 배우는 ios 9 프로그래밍 (닐스미스 지음/ 황반석 옮김)" 에 있는 예제를 참고하여 작성하였습니다.

+ Recent posts