## 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:

<Callout>

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.

</Callout>

```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
        // ...
    }
}
```
