How can I prepopulate the iCloud server? - swift

i am unable to decide on what would be the best possible way to go ahead with the task of
Having 50 large video files with 50 large images corresponding to those files and some related text prepopulated in iCloud so that when the user downloads the app , they have some videos already installed to watch and not have to start from scratch…
I am aware of how to upload text to iCloud using some code as below , but I do. not know how will I proceed with large media files, where will I store them , how will I point to them ?
If a user wants to play those videos, will those videos need to be downloaded to the user device ? Will this not increase the size of app significantly ? How can I avoid this ?
All in all I am totally lost as to how to deal with large size files, how to prepopulate my app with some of these files and how to add more files in future ,
Can any one please guide and point me to a plan of action I can take in this regard ...
The code I have to be able to prepare and upload some data to iCloud , but I do not know how to use them when using large media files, where will I store those files etc
func prepareCloudRecords(owner: String) -> [CKRecord] {
let parentName = objectID.uriRepresentation().absoluteString
let parentID = CKRecord.ID(recordName: parentName)
let parent = CKRecord(recordType: "Project", recordID: parentID)
parent["title"] = projectTitle
parent["detail"] = projectDetail
parent["owner"] = owner
parent["closed"] = closed
var records = projectItemsDefaultSorted.map { item -> CKRecord in
let childName = item.objectID.uriRepresentation().absoluteString
let childID = CKRecord.ID(recordName: childName)
let child = CKRecord(recordType: "Item", recordID: childID)
child["title"] = item.itemTitle
child["detail"] = item.itemDetail
child["completed"] = item.completed
child["project"] = CKRecord.Reference(recordID: parentID, action: .deleteSelf)
return child
}
records.append(parent)
return records
}
Then use it to upload to server on button click like so making a function, thanks
func uploadToCloud() {
if let username = username {
let records = project.prepareCloudRecords(owner: username)
let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
operation.savePolicy = .allKeys
operation.modifyRecordsCompletionBlock = { _, _, error in
updateCloudStatus()
}
cloudStatus = .checking
CKContainer.default().publicCloudDatabase.add(operation)
} else {
showingSignIn = true
}
}

Related

Swift saving new comments using CloudKit

I'm trying to make an app which stores a user's comment on CloudKit and then shows it to the other users. User simply enters his/her comment on a text field and clicks on a submit button to submit his/her comment (just like a restaurant app). However, I can't seem to find the correct way no matter what I try. Here is my code, I'd be very glad for any help as I've been stuck on this problem for some time now. Thank you very much in advance!
#IBAction func OnSubmitTouched(_ sender: UIButton) {
if (textField.text != ""){
let newComment = CKRecord(recordType: "Users")
let publicDB = CKContainer.default().publicCloudDatabase
newComment.setValue(textField.text!, forKey: "comment")
publicDB.save(newComment){
rec ,err in
if let error = err {
print(err.debugDescription)
return
}
publicDB.fetch(withRecordID: newComment.recordID){
rec, err in
print(rec!["comment"]!)
return
}
}
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "comment", predicate: predicate)
let operation = CKQueryOperation(query: query)
var commentRecords: [CKRecord] = []
operation.recordFetchedBlock = { record in
commentRecords.append(record)
}
operation.queryCompletionBlock = { cursor, error in
print(commentRecords)
}
CKContainer.default().publicCloudDatabase.add(operation)
}
}
You are getting a permission error because Users is a protected record type that CloudKit creates automatically for users of your app. You should name it something else and then it should work.
For example, you could make a Comment record type. This might need a field that references the current user. You can get the current userID with:
CKContainer fetchUserRecordIDWithCompletionHandler:
Here is the Apple documentation for this method.
It is also possible to use the Users record type, but you would have to find the existing userID from CloudKit as above then build a record around that.
See also this answer.

Can we access private document from google drive in iOS?

I am working on google drive api for fetching user's images/photos. But i am getting only public photos of user not Private. Can you please tell me is it possible to fetch private photos too ?
I was trying the same and came up with the solution.
Please give permissions by adding scopes.
private let scopes = [kGTLAuthScopeDrivePhotosReadonly, kGTLAuthScopeDriveReadonly]
Fetch Files:
let query = GTLQueryDrive.queryForFilesList()
query.pageSize = 10
query.q = "mimeType = 'image/png' or mimeType = 'image/jpeg' or mimeType = 'image/jpg'"
query.fields = "nextPageToken, files"
AppSingletonObj.service.executeQuery(
query,
delegate: self,
didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
)
for showing images in collection view, i used “file.thumbnailLink”
for fetching original images,
let fetcherURL = "https://www.googleapis.com/drive/v3/files/(file.identifier)?alt=media"
let fetcher = AppSingletonObj.service.fetcherService.fetcherWithURLString(fetcherURL)
fetcher.beginFetchWithCompletionHandler({ (data, error) -> Void in
if error == nil {
print("success")
}
else {
print("failure")
}
})

Swift Firebase Storage How to retrieve image with unknow name(NSUUID)

I am making a function to retrieve the url as the user Image. However, my upload image name function created by NSUUID. Therefore, I would not know what is the name of each user profile picture. How could I improve my code to get the user imgae for every user instead of hard coding the img name?
func getUserProfilePic(){
let uid = FIRAuth.auth()?.currentUser?.uid
let profilePath = refStorage.child("\(uid)").child("profilePic/xxxxx.jpg") // xxxxx = NSUUID
profilePath.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("got no pic")
} else {
let profilePic: UIImage! = UIImage(data: data!)
self.imageV.image = profilePic
print("got pic")
}
}
}
The path is uid/profilePic/--<-file name->--
upload function
func uploadingPhoto(){
let uid = FIRAuth.auth()?.currentUser?.uid
let imgName = NSUUID().UUIDString + ".jpg"
let filePath = refStorage.child("\(uid!)/profilePic/\(imgName)")
var imageData = NSData()
imageData = UIImageJPEGRepresentation(profilePic.image!, 0.8)!
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpg"
let uploadTask = filePath.putData(imageData, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
}else{
let downloadURL = metaData!.downloadURL()!.absoluteString
let uid = FIRAuth.auth()?.currentUser?.uid
let userRef = self.dataRef.child("user").child(uid!)
userRef.updateChildValues(["photoUrl": downloadURL])
print("alter the new profile pic and upload success")
}
}
I highly recommend using Firebase Storage and the Firebase Realtime Database together to store that UUID -> URL mapping, and to "list" files. The Realtime Database will handle offline use cases like Core Data as well, so there's really no reason to futz with Core Data or NSUserDefaults. Some code to show how these pieces interact is below:
Shared:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
Upload:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
Download:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Do something with downloaded data...
})
})
For more information, see Zero to App: Develop with Firebase, and it's associated source code, for a practical example of how to do this.
There can be two ways to go about this :-
1.) Store the Firebase Storage path of users profile_picture in your Firebase database and retrieve every time before you start downloading your profile picture.
2.) Store the file path in CoreData every time your user uploads a profile picture and hit that path every time to get file_Path to where you stored that user's profile_pic .
Storing the path :-
func uploadSuccess(metadata : FIRStorageMetadata , storagePath : String)
{
print("upload succeded!")
print(storagePath)
NSUserDefaults.standardUserDefaults().setObject(storagePath, forKey: "storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)")
//Setting storagePath : (file path of your profile pic in firebase) to a unique key for every user "storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)"
NSUserDefaults.standardUserDefaults().synchronize()
}
Retrieving your path, Every time you start downloading your image :-
let storagePathForProfilePic = NSUserDefaults.standardUserDefaults().objectForKey("storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)") as? String
Note :- i am using currentUser ID, you can use USER SPECIFIC id ,if you want to download multiple users profile pics, all you need to do is put their uid in place.

How to improve performance for large datasets with Realm?

My database has 500,000 records. The tables don't have a primary key because Realm doesn't support compound primary keys. I fetch data in background thread, then I want to display it in the UI on the main thread. But since Realm objects cannot be shared across threads I cannot use the record I fetched in the background. Instead I need to refetch the record on main thread? If I fetch a record out of the 500,000 records it will block the main thread. I don't know how to deal with it. I use Realm because it said it's enough quick. If I need refetch the record many times, is it really faster than SQLite? I don't want to create another property that combine other columns as primary key because the Realm database is already bigger than a SQLite file.
#objc class CKPhraseModel: CKBaseHMMModel{
dynamic var pinyin :String!
dynamic var phrase :String = ""
class func fetchObjects(apinyin :String) -> Results<CKPhraseModel> {
let realm = Realm.createDefaultRealm()
let fetchString = generateQueryString(apinyin)
let phrases = realm.objects(self).filter(fetchString).sorted("frequency", ascending: false)
return phrases
}
func save(needTransition :Bool = true) {
if let realm = realm {
try! realm.write(needTransition) {[unowned self] in
self.frequency += 1
}
}
else {
let realm = Realm.createDefaultRealm()
if let model = self.dynamicType.fetchObjects(pinyin).filter("phrase == %#", phrase).first {
try! realm.write(needTransition) {[unowned self] in
model.frequency += self.frequency
}
}
else {
try! realm.write(needTransition) {[unowned self] in
realm.add(self)
}
}
}
}
}
then I store fetched records in Array
let userInput = "input somthing"
let phraseList = CKPhraseModel().fetchObjects(userInput)
for (_,phraseModel) in phraseList.enumerate() {
candidates.append(phraseModel)
}
Then I want to display candidates information in UI when the user clicks one of these. I will call CKPhraseModel's save function to save changes. This step is on main thread.
Realm is fast if you use its lazy loading capability, which means that you create a filter that would return your candidates directly from the Realm, because then you'd need to only retrieve only the elements you index in the results.
In your case, you copy ALL elements out. That's kinda slow, which is why you end up freezing.

SWIFT: do I have someone set a record with different text every time?

So I made a chatroom and when someone sends a message they also add a Subscription in my cloud kit database but the problem is there cant be more then one of the same name that is a subscription and I want them to be able to set more subscriptions then one. Here is some code:
func setupCloudKitSubscription () {
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.boolForKey("subscribed") == false {
let predicate = NSPredicate(format: "TRUEPREDICATE", argumentArray: nil)
let subscription = CKSubscription(recordType: "Extra1", predicate: predicate, options: CKSubscriptionOptions.FiresOnRecordCreation)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertLocalizationKey = "New Sweet"
notificationInfo.shouldBadge = true
subscription.notificationInfo = notificationInfo
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveSubscription(subscription) { (subscription:CKSubscription?, error:NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
}else{
userDefaults.setBool(true, forKey: "subscribed")
userDefaults.synchronize()
You see how it says recordType: "Extra1" how can I made the "Extra1" different text every time someone makes a subscription? Thanks!
Your question is not completely clear. I think what you wanted to ask is that you want the subscription to send you a different message with each notification.
You could set it to display one or more fields of the record. For doing that you should use something like this:
notificationInfo.alertLocalizationKey = "Response: %1$#"
notificationInfo.alertLocalizationArgs = ["responseField"]
Then you also need this in your Localization.Strings file.
"Response: %1$#" = "Response: %1$#";