## Customization ### Customize Object Key Path You can customize your key path by defining a prefix resolver: ```swift import Amplify import AWSPluginsCore import AWSS3StoragePlugin // Define your own prefix resolver, that conforms to `AWSS3StoragePluginPrefixResolver` struct MyDeveloperDefinedPrefixResolver: AWSS3PluginPrefixResolver { // This function is called on every Storage API to modify the prefix of the request. func resolvePrefix( for accessLevel: StorageAccessLevel, targetIdentityId: String? ) async throws -> String { // Use "myPublicPrefix" for guest access levels if accessLevel == .guest { return "myPublicPrefix/" } // Use "myProtectedPrefix/{identityId}" and "myPrivatePrefix/{identityId}" respectively let accessLevelPrefix: String if accessLevel == .protected { accessLevelPrefix = "myProtectedPrefix/" } else { accessLevelPrefix = "myPrivatePrefix/" } // `targetIdentityId` is the value passed into the Storage request object if let identityId = targetIdentityId { return accessLevelPrefix + identityId + "/" } // `identityId` is the identity id of the current user let identityId = try await getIdentityId() return accessLevelPrefix + identityId + "/" } func getIdentityId() async throws -> String { let session = try await Amplify.Auth.fetchAuthSession() if let identityProvider = session as? AuthCognitoIdentityProvider { return try identityProvider.getIdentityId().get() } else { throw StorageError.authError("Unable to retrieve identity id", "", nil) } } } ``` Then configure the storage plugin with the resolver. ```swift let storagePlugin = AWSS3StoragePlugin(configuration: .prefixResolver(MyDeveloperDefinedPrefixResolver())) Amplify.add(storagePlugin) ``` Add the IAM policy that corresponds with the prefixes defined above to enable read, write and delete operation for all the objects under path *myPublicPrefix/*: ```xml "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::your-s3-bucket/myPublicPrefix/*", ] } ] ``` If you want to have custom *private* path prefix like *myPrivatePrefix/*, you need to add it into your IAM policy: ```xml "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::your-s3-bucket/myPrivatePrefix/${cognito-identity.amazonaws.com:sub}/*" ] } ] ``` This ensures only the authenticated users has the access to the objects under the path. #### Passthrough PrefixResolver If you would like no prefix resolution logic, such as performing S3 requests at the root of the bucket, create a prefix resolver that returns an empty string: ```swift func resolvePrefix( for accessLevel: StorageAccessLevel, targetIdentityId: String? ) async throws -> String { return "" } ``` #### Client validation You can also perform validation based on the access controls you have defined. For example, if you have defined Guests with no access then you can fail the request early by checking if the user is not signed in: This will only stop your app from making a call with an unauthenticated user. You must also set up your IAM policies to protect your resources. See [CLI Storage](/cli/storage/import) for more details. ```swift struct MyDeveloperDefinedPrefixResolver: AWSS3PluginPrefixResolver { func resolvePrefix( for accessLevel: StorageAccessLevel, targetIdentityId: String? ) async throws -> String { guard await Amplify.Auth.getCurrentUser() != nil else { throw StorageError.authError("User is not signed in", "", nil) } // Continue to resolve the prefix for the request // ... } } ```