Process for uploading image to s3 with AWS Appsync || iOS image uploading with Appsync - ios

I'm working on a new project that requires uploading attachments in the form of images. I'm using DynamoDB and AppSync API's to insert and retrieve data from database. As we are new to the AppSync and all the amazon services and database we are using for the app i'm little bit confused about the authentication process. Right now we are using API key for authentication and I have tried these steps to upload image to s3.
1 Configue the AWSServiceManager with static configuration like :-
let staticCredit = AWSStaticCredentialsProvider(accessKey: kAppSyncAccessKey, secretKey: kAppSyncSecretKey)
let AppSyncRegion: AWSRegionType = .USEast2
let config = AWSServiceConfiguration(region: AppSyncRegion, credentialsProvider: staticCredit)
AWSServiceManager.default().defaultServiceConfiguration = config
2 Uploading picture with this method : -
func updatePictureToServer(url:URL, completion:#escaping (Bool)->Void){
let transferManager = AWSS3TransferManager.default()
let uploadingFileURL = url
let uploadRequest = AWSS3TransferManagerUploadRequest()
let userBucket = String(format: "BUCKET")
uploadRequest?.bucket = userBucket
let fileName = String(format: "%#%#", AppSettings.getUserId(),".jpg")
uploadRequest?.key = fileName
uploadRequest?.body = uploadingFileURL
transferManager.upload(uploadRequest!).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask<AnyObject>) -> Any? in
if let error = task.error as NSError? {
if error.domain == AWSS3TransferManagerErrorDomain, let code = AWSS3TransferManagerErrorType(rawValue: error.code) {
switch code {
case .cancelled, .paused:
break
default:
print("Error uploading: \(String(describing: uploadRequest!.key)) Error: \(error)")
}
} else {
print("Error uploading: \(String(describing: uploadRequest!.key)) Error: \(error)")
}
completion(false)
return nil
}
_ = task.result
completion(true)
print("Upload complete for: \(String(describing: uploadRequest!.key))")
return nil
})
}
3 And finally i'm able to see the uploaded image on the S3 bucket
But i'm concerned about how to save the url of the image and how to retrieve the image because when i have to make the buket PUBLIC to retrieve the image and i don't think that's a good approach, plus is it necessary to have a Cognito user pool because we aren't using Cognito user pool yet in our app and not have much knowledge about that too and documents are not helping in practical situations because we are implementing ti for the first time so we need some little help.
So two question : -
Proper procedure to use for uploading and retrieving images for S3 and AppSync.
Is it necessary to use Cognito user pool for image uploading and retrieving.
Thanks
Note: Any suggestion or improvement or anything related to the AppSync, S3 or DynamoDB will be truly appreciated and language is not a barrier just looking for directions so swift or objective-c no problem.

You need per-identity security on the bucket using Cognito Federated Identities which gives each user their own secure bucket. You can leverage the AWS Amplify to set this up for your project with $amplify add auth and selecting the default config, then $amplify add storage which configures that bucket and pool with appropriate permissions to use private uploads.
For more info checkout the repo: https://github.com/aws-amplify/amplify-cli

Related

AWSS3TransferUtilityErrorDomain Code=2 on iOS Swift While trying to upload Image/pdf

I am trying to upload pdf file or image file to AWSS3 bucket but I am getting AWSS3TransferUtilityErrorDomain Code=2 error. Please note I already have checked region and it is correct. Also I have verified that I am using correct accessKey and secretKey I also have visited below mentioned links with no luck:
(https://github.com/aws-amplify/aws-sdk-ios/issues/2553.)
(https://github.com/aws-amplify/aws-sdk-ios/issues/604)
(https://github.com/aws-amplify/aws-sdk-ios/issues/420)
(https://github.com/aws-amplify/aws-sdk-ios/issues/103)
(Upload image to S3 with Amazon Educate Starter Account)
(About permission in S3 file transfer)
(Swift iOS: Unable to Upload Image to AWS S3)
(AWSS3TransferUtilityErrorDomain Code=2 on ios)
My code to upload file is below:
let credentials = AWSStaticCredentialsProvider(accessKey: “accessKey” , secretKey: “secretKey”)
let configuration = AWSServiceConfiguration(region: AWSRegionType.APSouth1 , credentialsProvider: credentials)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { (task, progress) in
DispatchQueue.main.async(execute: {
// Update a progress bar
print("Task: \(task)")
print("Progress: \(progress)")
})
}
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if let error = error {
CommonLoader.hide()
SCLAlertView().showError("Error", subTitle: error.localizedDescription)
return
}
// Do stuff after success
})
}
let transferUtility = AWSS3TransferUtility.default()
// contentType —-> “image/jpeg” for images && “application/pdf” for pdf files
transferUtility.uploadData(data, bucket: s3BucketName, key: remoteName, contentType: contentType, expression: expression, completionHandler: completionHandler).continueWith { (task) -> Any? in
if let error = task.error {
// error case
}
else {
if !task.isFaulted && task.result != nil {
// success case
}
}
return nil
}
After a lot of search and reading documentation I am able to solve this issue.
In my case there were two strange things, one with same credentials and bucket on Android it was working.
But on iOS exactly same code was working in Dubai but not in Pakistan.
I solved the issue by just adding the region to project info.plist file as mentioned below:
Please note in my case region was ap-south-1 but you need to put it here yours, you can check region from Amazon S3 Endpoints and then find corresponding region value to use in your info.plist. Hope this will help someone and save time. Happy coding. cheers!
<key>S3TransferUtility</key>
<dict>
<key>Default</key>
<dict>
<key>Region</key>
<string>"ap-south-1"</string>
</dict>
</dict>

Swift 3 Azure Blob Storage Data (Image, Video) upload with SAS

I'm searching for an useful Swift 3 Azure Blob Storage example which I could use to upload some data(image, video). For now, I can insert records into my Mobile Service database and there I generate a SAS and I get it back to my iOS application. Now I need to know how to upload to Azure Blob Storage with help of that SAS. I successfully implemented the same for Android and it works, but somehow I have troubles to find any useful information for "SWIFT" and how to use the "SAS"!
Any code examples how to upload with SAS in Swift are much appreciated.
Regards,
Adam
For those who have the same problem as I had: This is a working example in Xcode 8 and Swift 3. You have to include the "Azure Storage Client Library" into your project.
//Upload to Azure Blob Storage with help of SAS
func uploadBlobSAS(container: String, sas: String, blockname: String, fromfile: String ){
// If using a SAS token, fill it in here. If using Shared Key access, comment out the following line.
var containerURL = "https://yourblobstorage.blob.core.windows.net/\(container)\(sas)" //here we have to append sas string: + sas
print("containerURL with SAS: \(containerURL) ")
var container : AZSCloudBlobContainer
var error: NSError?
container = AZSCloudBlobContainer(url: NSURL(string: containerURL)! as URL, error: &error)
if ((error) != nil) {
print("Error in creating blob container object. Error code = %ld, error domain = %#, error userinfo = %#", error!.code, error!.domain, error!.userInfo);
}
else {
let blob = container.blockBlobReference(fromName: blockname)
blob.uploadFromFile(withPath: fromfile, completionHandler: {(NSError) -> Void in
NSLog("Ok, uploaded !")
})
}
}

Download and use files stored in Firebase Storage from different projects

I'm using Firebase Storage to store images in FB, I'm saving download URL in Realtime DB and to retrieve some photos from the storage, I'm using this code:
let storage = FIRStorage.storage()
imageRefInDB.observeEventType(.Value, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value as! String
// Create a storage reference from the URL
let storageRef = storage.referenceForURL(downloadURL)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print(error.debugDescription)
} else {
let downloadedImage = UIImage(data: data!)
}
}
})
My problem is I wanna use some of my photo that are inside another Firebase project. For example, the download URLs will be:
https://firebasestorage.googleapis.com/v0/b/PROJECT1.appspot.com/o/someMedia&token
When in my current project, the bucket is different like :
https://firebasestorage.googleapis.com/v0/b/PROJECT2.appspot.com/o/someMedia&Token
When I try to using the PROJECT1 files in my current PROJECT2, I'm getting the following error:
reason: 'Provided bucket: PROJECT1.appspot.com does not match bucket specified in FIRApp configuration: PROJECT2.appspot.com
Is there a way to enable downloading theses files from other projects like a setting in Firebase without retrieving them with regular URL downloads?
Thanks for your help!
I tried that once and it lead to an unhappy time. In my case I had two different buckets for my images, so I had to find an alternate solution.
If you have only a single bucket, you can try configuring that bucket in your FIRApp:
let firOptions = FIROptions(googleAppID: googleAppID, bundleID: bundleID, GCMSenderID: GCMSenderID, APIKey: nil, clientID: nil, trackingID: nil, androidClientID: nil, databaseURL: databaseURL, storageBucket: storageBucket, deepLinkURLScheme: nil)
FIRApp.configureWithName("anotherClient", options: options)
let app = FIRApp(named: "anotherClient")
let storageRef = FIRStorage.storage(app: app!).reference()
Taken from this answer, so you may need to modify it to suit your needs
With that app, you can then use the Firebase Storage API as you're already doing to download the image data.
If you have multiple buckets (as I had), you either have to configure multiple apps or (as I did) handle downloading the image from the downloadURL yourself (without using the Firebase Storage API):
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
let data = NSData(contentsOfURL: downloadURL) //make sure your image in this url does exist, otherwise unwrap in a if let check
dispatch_async(dispatch_get_main_queue(), {
let downloadedImage = UIImage(data: data!)
});
}

Amazon Cognito with iOS: Access to Identity Forbidden

Thanks for the help in advance!
I am having some trouble getting Amazon Cognito to store/synchronize data properly.
On the dataset.synchronize() line (which does not store the data in Cognito), I get a large output error (with ID starred out) such as:
AWSCredentialsProvider.m line:429 | __73-[AWSCognitoCredentialsProvider
getCredentialsWithCognito:authenticated:]_block_invoke | GetCredentialsForIdentity
failed. Error is [Error Domain=com.amazonaws.AWSCognitoIdentityErrorDomain
Code=10 "(null)" UserInfo={__type=NotAuthorizedException, message=Access to
Identity '*****' is forbidden.}]
The cognitoID is not nil, and returns properly (and matches the values I can read online)
For instance, after authenticating with Facebook, I perform the following:
if (FBSDKAccessToken.currentAccessToken() != nil)
{
let fbCognitoToken = FBSDKAccessToken.currentAccessToken().tokenString
credentialsProvider.logins = [AWSCognitoLoginProviderKey.Facebook.rawValue: fbCognitoToken]
// Retrieve your Amazon Cognito ID
credentialsProvider.getIdentityId().continueWithBlock { (task: AWSTask!) -> AnyObject! in
if (task.error != nil) {
print("Error: " + task.error!.localizedDescription)
}
else {
// the task result will contain the identity id
let cognitoId = task.result
//checking if cognito was successful, if true, sets success condition to true to prepare for segue into app
if cognitoId != nil{
print (cognitoId)
cognitoSuccess = true
let syncClient = AWSCognito.defaultCognito()
let dataset = syncClient.openOrCreateDataset("User_Data")
dataset.setString("test#test.com", forKey:"Email")
// credentialsProvider.refresh()
dataset.synchronize()
} }return nil}}
I can read data from Facebook correctly, and all authentication occurred correctly from what I can tell. I suspect there is something simple that is at the root here, but after spending several days, I cannot figure it out! Using the IAM checker in the AWS portal returns all "green checks" for Cognito functions, so I am sure this not a permissions issue on the server-side, either.
Thanks again for any insight you might have!
Edit:
Before the chunk of code above, I call:
let credentialsProvider = self.initializeCognito()
which runs (identity pool ID starred out):
func initializeCognito () -> AWSCognitoCredentialsProvider
{
let credentialsProvider = AWSCognitoCredentialsProvider(
regionType: AWSRegionType.USEast1, identityPoolId: "******")
let defaultServiceConfiguration = AWSServiceConfiguration(
region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = defaultServiceConfiguration
return credentialsProvider
}
That exception can be thrown when you're trying to get credentials for an authenticated id without giving any provider token linked to it. Cognito requires at least one to be given.
Can you check that you're including the facebook token during the GetCredentialsForIdentity call that's failing? If not, I'd guess that's your issue.
Edit:
Since you are using AWSCognito.defaultCognito(), it might help to follow the example on this docs page to make sure the sync client uses the right credentials provider:
let configuration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration
Ended up figuring out the answer-- when I first set up AWS and was following some of Amazon's guides, I had placed code to create a new credentialsProvider in the application's App Delegate. I forgot about it, and then was trying later on to initialize another credentialsProvider. This confusion created the issues, and removing the initialization in App Delegate fixed the authentication problems.

Get a reference to the AWSS3 object

I'm uploading a photo from my iOS app to Amazon S3 successfully. I need to get the publicly accessible URL for that photo. Instead of building the URL manually, I use the following way to do that.
let transferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.upload(uploadRequest).continueWithBlock { task in
if let error = task.error {
print("Upload failed: \(error.code) - \(error.localizedDescription)")
}
if let exception = task.exception {
print("Upload failed: \(exception)")
}
if task.result != nil {
print("Successfully uploaded!")
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: CognitoRegionType, identityPoolId: CognitoIdentityPoolId)
let configuration = AWSServiceConfiguration(region: DefaultServiceRegionType, credentialsProvider:credentialsProvider)
let aws3 = AWSS3(configuration: configuration)
let publicURL = aws3.configuration.endpoint.URL.URLByAppendingPathComponent(uploadRequest.bucket!).URLByAppendingPathComponent(uploadRequest.key!)
print(publicURL)
}
return nil
}
This works well and I get proper the public URL.
https://s3-ap-northeast-1.amazonaws.com/myapp/DAEF70E9-495A-40B4-B853-3B337486185D-4988-00000E22AB8E25A6.jpg
I have two problems.
1). Initializing it this way AWSS3(configuration: configuration) is deprecated now.
2). This while initializing code already happens inside the App Delegate's didFinishLaunchingWithOptions method.
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: CognitoRegionType, identityPoolId: CognitoIdentityPoolId)
let configuration = AWSServiceConfiguration(region: DefaultServiceRegionType, credentialsProvider:credentialsProvider)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration
However trying to call the endpoint property from this configuration returns nil.
So what I'm looking to do is this. I don't want to repeat the initializing code in both App Delegate and here. So if there's a way to get a reference to the already initialized object in App Delegate, I'd love to know.
I think you could use the following API: https://docs.aws.amazon.com/AWSiOSSDK/latest/Classes/AWSS3.html#//api/name/registerS3WithConfiguration:forKey:
The SDK would hold the object for you and can always fetch it by using S3ForKey: mentioned here: https://docs.aws.amazon.com/AWSiOSSDK/latest/Classes/AWSS3.html#//api/name/S3ForKey:
There are code snippets in the API reference demonstrating the usage.
-Rohan
I was actually able to get an instance of S3 object with AWSS3.defaultS3(). So I could construct the public URL like this.
let publicURL = AWSS3.defaultS3().configuration.endpoint.URL.URLByAppendingPathComponent(uploadRequest.bucket!).URLByAppendingPathComponent(uploadRequest.key!)

Resources