How to give each user a specific folder in the firebase storage - swift

I have made an app that requires login authorization, and I want each user who has signed up for the app to have their own folder in the firebase storage. That way, all the data that gets passed to firebase is organized in separate folders for each user; keeping everything organized and the data easily accessible. I am wondering how to do this but am lost. I currently have firebase set up for my app but am struggling to figure this part out.

To allow users to only read/write their own files, have a look at the section on content-owner only access in the documentation on security rules.
to upload files to a user-specific folder, start by setting up a reference to the user folder:
let storageRef = storage.reference()
if Auth.auth().currentUser != nil {
let uid = Auth.auth().currentUser.uid
let userRef = storageRef.child()
...
And then follow the rest of the documentation on uploading files.

Related

How to solve Swift FileManager couldn’t open file because of permission and implement file access

I made a music app for macOS that need the access of the file on disk. I can get the proper url, but I can't access the file. To figure out that problem, here I create a concise code snippet to concentrate on the question itself.
.onAppear {
var url = FileManager.default.homeDirectoryForCurrentUser
url.appendPathComponent("Downloads/Test.txt")
var text: String = ""
do {
text = try String(contentsOf: url)
} catch {
print("ERROR: \(error.localizedDescription)")
}
}
The output would be like this
ERROR: The file “Test.txt” couldn’t be opened because you don’t have permission to view it.. So I noticed App Sandbox in Targets > Signing & Capabilities. But at the list of file access (as you can see in the following picture), there's only User Selected File, Downloads Folder, Pictures Folder, Music Folder and Movie Folder. So I come up with 3 questions:
What that User Selected File means? Is that means that the user can select some specific file for my app to access? If it is, how can user selects them?
How can I get access to other folders like Desktop Folder if I remain the App Sandbox capability here?
I also noticed the trash icon on right-upper corner. If I delete App Sandbox, I can access every folder I want with the user's agreement by a default system pop-up. But what the deletion of App Sandbox means? Does it make my app "untrusted" somehow when user install it or others? Does it cause any security problems?
And It couldn't be better if you can tell me the conventions major developers follow when meet this problem. I would highly appreciate your help.

How to retrieve name of user in firebase

I am creating an app (Xcode, swift) that has a profile page for each user and I want their name to appear on that page.
I have been able to get their email address through:
let email : String = (Auth.auth().currentUser?.email)!
How would I gather the users name? I have the users UID as well.
I am using firebase by the way
If you are not using Google or Facebook to log in with firebase, You need to manually create the profile for each user. See Update a user's profile
If you're using a social provider to sign in, you can get the display name from that provider through Firebase with:
Auth.auth().currentUser?.displayName
If you're signing in with another provider, the display name won't automatically be set, and you will (as Abdullah answered) have to create your own registration system where the user enters their name - and you then store it in the displayName property of Firebase Authentication.
To achieve what you requested, you either have to use a social auth provider (such as Google or Facebook) or change it yourself from the client, as the other answers suggest.
First of all, you would have to create a changeRequest, using the following code
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
Once the change request is created, you can change whatever basic information you need to (either the photo URL or the display name) with the following code:
changeRequest?.displayName = "Lorem ipsum"
changeRequest?.photoURL = "https://your_link/path_to_image.png"
Finally, you must send the change request to Firebase, which will handle it and possibly return an error for you to handle.
changeRequest?.commitChanges { error in
if let error = error {
print(error.localizedDescription)
// You can handle the given error here
return
}
}
As others have already pointed out, you can find this and more information on the official on the official Firebase docs website.

Firebase Offline Support: upload posts while user is offline and sync when user comes online in iOS Swift app

I am using firebase in an iOS-Swift project in which I have to enable offline support for uploading posts, in the post there is a picture and caption just like Instagram, so what I want is when user is offline and he/she wants to upload a post, his/her picture get saved in cache and when user comes online that photo get uploaded and give back a download url that we can use for saving posts-details it in database.
sample code is:
let photoIDString = UUID().uuidString
let storageRef = Storage.storage().reference(forURL: "storage ref URL").child("posts").child(photoIDString)
storageRef.putData(imageData, metadata: nil, completion: { (metadata, error) in
guard let metadata = metadata else {
return
}
if error != nil {
return
}
storageRef.downloadURL(completion: { ( url, error ) in
guard let downloadURL = url else {
return
}
let photoUrl = downloadURL.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl)
})
}
)
I want to know what changes should I make in my code to provide the offline capability. Any code snippet will help more.
The problem is better view as re-send to server when there is an error.
For your offline case, you can check if the error return is a network error, or manually check network connection availability.
You can create a re-send array of object
e.g
var resendList : [YourObjectType]
// when failed to send to server
resendList.append(yourFailedObject)
And then, 2 solutions:
Check the network connectivity manually and reupload in when the app become active in func applicationDidBecomeActive(_ application: UIApplication) in appDelegate. For checking connectivity you can try the method here: Check for internet connection with Swift But this has a problem that, the user has to go out the app and back again with network connected
Keep track(listen to notification) on the connectivity change, using a suggestion method by https://stackoverflow.com/a/27310748/4919289 and reupload it to server
and loop through all objects in resendList and re-upload again.
I am not an iOS developer, but I can share logical flow and some references.
When user clicks on upload: Check if network is available?
if yes: upload the post.
if no:
save the post to app storage or offline database
set broadcast receiver to receive broadcast when device comes online. This link may be helpful.
upload post when device comes online.
If you are looking for solution that is offered by Firebase, you may find more details here.
Firebase offers you plenty of ways to do this in their documentation. https://firebase.google.com/docs/database/ios/offline-capabilities
When uploading to the firebase server, it will queue itself and wait until it has a internet connection again to upload. If this happens to timeout or you want to do it your own way just attempt to upload with a completionHandler on the setValue or updateChild functions - if not successfully and the error message is because of internet, add it to a local cache to the phone with the data and the path to the firebase server.
onLoad, attempt the same upload again until it succeeds, once it succeeds - clear the local cache.

Firebase iOS SDK authentication with private Google storage

I am testing out using the Firebase SDK for iOS/macOS in my app (macOS app). i have installed the SDK´s using:
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :tag => '4.8.2'
pod 'FirebaseAuth', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :tag => '4.8.2'
pod 'FirebaseStorage', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :tag => '4.8.2'
The installation works well and I can configure my app in AppDelegate using [FIRApp configure];
I wonder if I can use the SDK to log in the user to his/hers private Google Cloud storage (GCS)? I understand I can use the SDK for storing to GCS in the apps storage, but it would be nice to log in to the users own GCS to retrieve a list of buckets and files. If anyone has an example as for how to do this I would appreciate it. All examples I find are for anonymous storage logins.
Update:
I could specify that I was hoping that Firebase SDK would contain an authentication method that allowed me access to my own Google cloud storage account. Perhaps Firebase is not the right choice for this, but then I would be very interested in suggestions for alternative SDKs for Swift/objective-c login/upload/download to Google cloud storage.
You can indeed use the Firebase SDK for iOS to work with Firebase Cloud Storage (which in fact stores data in Google Cloud Platform Cloud Storage's buckets), using both Swift and Objective-C.
Firebase Cloud Storage in iOS
Regarding the usage of Cloud Storage buckets in your Firebase application, you can get started with this documentation page. First of all, you have to set up the proper security rules to the bucket: you can allow public access, access to only authenticated users, or even per-userID access. There are some sample rules that you can use to start working with this.
Once you have set up the appropriate access for Storage buckets (if each user has its own bucket, then I assume each user will have a GCP account with a private bucket and they will have to set up the configuration access themselves, as you will not have access to them), you can add the Cloud Storage dependencies to your iOS app:
pod 'Firebase/Core'
pod 'Firebase/Storage'
Then run pod install and you can already create the reference to Cloud Storage after initializing Firebase in your app (here you have a Swift sample code, but you can have a look at the Objective-C samples in the documentation too):
// Import and set up Firebase
import Firebase
FirebaseApp.configure()
// Create a storage reference
let storage = Storage.storage()
let storageRef = storage.reference()
// Refer to a child directory or even a file
let folderRef = storageRef.child("my_folder")
var fileRef = folderRef.child("my_file.txt")
And once you have all this, you can proceed to more complex guides, such as uploading files, downloading files or (important) handling errors. Bear in mind that these are just some examples of the things you can do following the step-by-step documentation, but feel free to move through all the pages in order to have a deeper understanding about how all this works.
Firebase Authentication for Cloud Storage in iOS
Also, regarding authentication, you can follow the same rules that you are probably already using for the rest of your Firebase application. Let me share this other page talking about some mechanisms to provide Firebase Authentication, and specifically how to provide Firebase Authetication on iOS.
I'm not sure I fully understand what you're asking. But if I do... This may help. I've used Google Storage to save photos. To access those photos I needed to store the URL to locate those photos. I did this in the Firebase Realtime Database. If you store a different type of file to a GCS, all you need is that URL to retrieve the data.
if let photoData = UIImageJPEGRepresentation(jpegRepresentation!, 1.0) {
storePhoto(photoData, angel.name!, completion: { (url, err) in
if err != nil {
print(err?.localizedDescription)
angelToSave["photo"] = nil
myAngelsRef.updateChildValues(angelToSave, withCompletionBlock: { (error, ref) in
if error != nil {
completion(error!)
} else {
completion(nil)
}
})
} else {
// ### HERE ####
angelToSave["photo"] = url?.absoluteString
angelNameRef.updateChildValues(angelToSave)
completion(nil)
}
})
}
func storePhoto(_ photo: Data, _ name: String, completion: #escaping (_ result: URL?, _ error: NSError?) -> Void) {
let storageRef = Storage.storage().reference().child(name)
storageRef.putData(photo, metadata: nil) { (storageMetaData, err) in
if err != nil {
completion(nil, NSError(domain: (err?.localizedDescription)!, code: 0, userInfo: nil))
} else {
completion(storageMetaData?.downloadURL(), nil)
}
}
}
After I saved the photo I was able to get the URL location and save that to an object I stored in the RTDB. Now when I pull the data from the use's RTDB I get the URL for the Storage data.

When do I have to connect to realm object server in my swift code with regards to controllers, functions and queries

Currently I am using this code from the realm webpage to connect to the realm mobile server.
func setupRealm() {
// Log in existing user with username and password
let username = "admin" // <--- Update this
let password = "admin" // <--- Update this
SyncUser.logIn(with: .usernamePassword(username: username, password: password, register: false), server: URL(string: "http://127.0.0.1:9080")!) { user, error in
guard let user = user else {
fatalError(String(describing: error))
}
DispatchQueue.main.async {
// Open Realm
let configuration = Realm.Configuration(
syncConfiguration: SyncConfiguration(user: user, realmURL: URL(string: "realm://127.0.0.1:9080/~/realmtasks")!)
)
self.realm = try! Realm(configuration: configuration)
}
}
}
In my code when and where do I have to use this connection. My current observation is:
1, in the ViewController in every function of viewDidLoad, viewWillAppear, etc I have to run the above code
before every query I have to run the code again
Is there a clever way of only having to connect once for the whole project or once for the whole ViewController? This is to consider that I am updating and querying the realm mobile database in different ViewControllers in different functions.
It shouldn't be necessary for you to log in the user any more than once per application session at most.
I recommend you create a manager class, perhaps a singleton or otherwise an instance you can pass throughout your app, that handles acquiring and holding on to the user object. This manager can be instantiated when your application launches.
If your user hasn't yet logged in before ever, or has previously logged out, once they enter their credentials you can then make the SyncUser.logIn() call to get the user. Once you have the user you can store it in the manager. Your view controllers can then ask this manager for the user object so they can use it to open their Realms.
If your user has logged in before, you can simply get the user (which is persisted between app launches) through SyncUser.current. The manager can handle deciding between getting the user by logging in and getting it from SyncUser.current so your view controllers don't need to concern themselves with this detail.
Another possible feature you might want to build is a way for view controllers who want a user to register on the manager to be notified if a user is logged in, so they can immediately open up Realms and perform other work.