Get a reference to the AWSS3 object - ios

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!)

Related

Unable to get the listing of files / objects in S3 bucket ; iOS - Swift

I am trying to get the list of objects in an S3 bucket. Below is the code but it doesn't seems to be working , neither is it throwing an error. However, the S3 object seemed to be (0x000000000) uninitialised.
I am not sure what's the mistake because the configuration is working for uploading a file to the bucket.
class Configuration {
let accessKey = Constants.AWS_KEY
let secretKey = Constants.AWS_SECRET
let bucket = Constants.AWS_BUCKET
static let instance = Configuration()
private init() {
let credentialsProvider = AWSStaticCredentialsProvider(accessKey: accessKey, secretKey: secretKey)
let configuration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
AWSS3.register(with: configuration!, forKey: "defaultKey")
}
}
class S3Browser {
static let configuration = Configuration.instance
static func getList(path:String) {
let s3 = AWSS3.s3(forKey: "defaultkey")
let listRequest: AWSS3ListObjectsRequest = AWSS3ListObjectsRequest()
//listRequest.prefix = path
listRequest.bucket = Constants.AWS_BUCKET
s3.listObjects(listRequest).continueWith { (task) -> AnyObject? in
for object in (task.result?.contents)! {
print("Object key = \(object.key!)")
}
return nil
}
}
}
All the keys and secrets are working for upload.
Kindly share some pointers.
You can check your permissions on AWS. Just because you have permission to upload doesn't mean you have permission to list the files in the S3 bucket.
How do you know the upload is working? Have you browsed in a web browser, the bucket at the AWS S3 site?

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

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

AWS iOS SDK AWSServiceManager multiple service configurations

I've integrated the AWS iOS SDK (v.2.3.6) into my application. It works fine and good, except that I've noticed that defaultServiceManager has a disclaimer:
"You should use this singleton method instead of creating an instance of the service manager".
I ordinarily wouldn't have an issue with this, except it's defaultServiceConfiguration is immutable:
"This property can be set only once, and any subsequent setters are ignored."
I have a requirement that a service configuration (ie. identityPoolId + region) be able to change at runtime.
What are the possible ways around this? I'd love to be able to just reset the service configuration at any point, but that's unlikely given what the documentation says.
You should not mutate the default service configuration. Instead, each service client provides the following class methods:
+ register[ServiceClientName]WithConfiguration:forKey:
+ [ServiceClientName]ForKey:
For example, for AWSS3TransferUtility, they are:
+ registerS3TransferUtilityWithConfiguration:forKey:
+ S3TransferUtilityForKey:
In this way, you can pass a different service configuration for each service client in the runtime. By following this pattern, you can avoid the unintentionally "polluted" default service configuration bugs that can be very difficult to debug.
To use the custom configurations with each upload you can create temporary access key and secret with a session. Then you can use those keys to upload your file. Below is the code snippet
/// This function is used to authorize user with AWS.
private func connectWithAWS() -> AWSServiceConfiguration? {
/// Simple session credentials with keys and session token.
let credentialsProvider = AWSBasicSessionCredentialsProvider.init(accessKey: "TEMPORARY ACCESS KEY", secretKey: "TEMPORARY SECRET KEY", sessionToken: "TEMPORARY SESSION")
/// A service configuration object.
guard let configuration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: credentialsProvider) else {
return nil
}
return configuration
}
func uploadFileToS3() {
/// Get configurations for bucket
guard let configuration = connectWithAWS() else {
///AWSServiceConfiguration Not Initialised.
return
}
///Check if a AWSS3TransferUtility already exist for current access key or not.
let trans = AWSS3TransferUtility.s3TransferUtility(forKey: "TEMPORARY ACCESS KEY")
if trans == nil {
/// If AWSS3TransferUtility is nil than create new for a access id
///
AWSS3TransferUtility.register(with: configuration, transferUtilityConfiguration: nil, forKey: "TEMPORARY ACCESS KEY") { (err) in
print("Error in AWSS3TransferUtility.register: ->>> \(err?.localizedDescription ?? "")")
}
}
///
/// Check if a AWSS3TransferUtility already exist for current access key or not.
guard let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "TEMPORARY ACCESS KEY") else {
return
}
///
/// Start Uploading process
///
let expression = AWSS3TransferUtilityUploadExpression()
expression.setValue("public-read", forRequestHeader: "x-amz-acl")
let s3BucketName = "BUCKET NAME"
let url = URL.init(fileURLWithPath: "fileURL")
transferUtility.uploadFile(url, bucket: s3BucketName, key: "fileName", contentType: "contentType", expression: expression) { (task, error) in
if error != nil {
print("Upload failed ❌ (\(error!))")
return
}
if task.status == AWSS3TransferUtilityTransferStatusType.completed {
let s3URL = "https://\(s3BucketName).s3.amazonaws.com/\(task.key)"
print("Uploaded to:\n\(s3URL)")
return
} else {
print("Not uploaded")
}
}.continueWith { (task) -> Any? in
if let error = task.error {
print("Upload failed ❌ (\(error))")
}
if task.result != nil, task.isCompleted == true {
let s3URL = "https://\(s3BucketName).s3.amazonaws.com/\(task.result!.key)"
print("Uploading Start of : \(s3URL)")
} else {
print("Unexpected empty result.")
}
return nil
}
}

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.

AWS Getting Started With Swift (Major Issues)

I am fairly new to Swift programming, and new to developing a mobile application in general. I tried to follow the following getting started guide here: https://github.com/aws/aws-sdk-ios
What I am currently attempting to do is create the component of my application which creates user accounts, and allows users to log in.
I have followed the ReadMe, but when I get to the following portion of code to make a call to AWS:
let credentialsProvider = AWSCognitoCredentialsProvider(
regionType: CognitoRegionType,
identityPoolId: CognitoIdentityPoolId)
let configuration = AWSServiceConfiguration(
region: DefaultServiceRegionType,
credentialsProvider: credentialsProvider)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration
let dynamoDB = AWSDynamoDB.defaultDynamoDB()
let listTableInput = AWSDynamoDBListTablesInput()
dynamoDB.listTables(listTableInput).continueWithBlock{ (task: AWSTask!) -> AnyObject! in
let listTablesOutput = task.result as AWSDynamoDBListTablesOutput
for tableName : AnyObject in listTablesOutput.tableNames {
println("\(tableName)")
}
return nil
}
I try to run my program, everything compiles, but stops at the line: let listTablesOutput = task.result as AWSDynamoDBListTablesOutput
When I say stops, I mean that I get a
Thread 6:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP,subcode=0x0)
Overall, I have spent hours reading documentation but it has not been able to answer any of my questions. Do we need to use DynamoDB to allow users to create an account and log in? If so, what exactly is Cognito for?
Also for the variables, shown in the code above, what exactly is credentialsProvider? Where do we get that information from?
Thank you in advance to anyone who can help me out in this matter.
you should use
let listTablesOutput = task.result as! AWSDynamoDBListTablesOutput
instead of
let listTablesOutput = task.result as AWSDynamoDBListTablesOutput ,
the compiler should warn you that if you compile it with the latest version of Xcode 6.
There is a DynamoDB Sample Project written in Swift which may be helpful.

Resources