amplify-swift icon indicating copy to clipboard operation
amplify-swift copied to clipboard

Removing of internal constraint on AmplifyOutputsData in Swift implementation.

Open sbelbin opened this issue 10 months ago • 11 comments

Is your feature request related to a problem? Please describe.

At times a Swift application needs to do the following for itself:

  1. Configure AWS Amplify Gen 2 Swift application using AmplifyOutputsData.init since these settings are stored in a secure & encrypted manner and other than in JSON file (i.e. amplify_outputs.json) or JSON representation.

  2. Fetching these configuration settings so that the application's behavior can be adapted based on those settings or present them to the application's users.

Showing to users the AWS region, authentication properties & policies, name of the AWS S3 bucket & region, ... can be useful for them. Additionally, the application can have specific behaviors based on the values of those settings.

Describe the solution you'd like

The removal of the @_spi(InternalAmplifyConfiguration) on the struct AmplifyOutputsData as well as various sub-structures and functions.

Doing this will make this accessible to Swift developers using AWS Amplify Gen 2.

Describe alternatives you've considered

What I've done is to copied the code of struct AmplifyOutputsData as well as various sub-structures and functions within my application code to avoid naming collision with Amplify module. But, where the @_spi(...) directive is commented out.

I've tried to workaround by using a different import directive the application such as @_spi(InternalAmplifyConfiguration) import Amplify, but Swift fails to compile stating that the AmplifyOutputsData is internal.

Is the feature request related to any of the existing Amplify categories?

No response

Additional context

The first point of being able to use AmplifyOutputsData.initis practical for those such as myself when developing an applications used the medical domain, since our best practices is store configuration in a secure manner such as iOS Keychain.

In that context being able to initialize a AmplifyOutputsData object with the appropriate settings without having to compose into an intermediary JSON representation is practical.

Thanks for your consideration.

sbelbin avatar Feb 20 '25 19:02 sbelbin

AmplifyOutputsData is marked internal because it is not considered stable. We may at times, change the internal structure, variable names, etc within AmplifyOutputsData. If we were to make this public, any changes would require a major release.

Android has a way to pass a json string, instead of a file, so this may be something that Swift can look into adding as well. You could store the json string however you would like. However, I would like to assure you that no data within the amplify_outputs.json file is considered private. There should be no need to encrypt these values.

I'll go ahead and mark this as a feature request.

tylerjroach avatar Feb 20 '25 20:02 tylerjroach

This has been identified as a feature request. If this feature is important to you, we strongly encourage you to give a 👍 reaction on the request. This helps us prioritize new features most important to you. Thank you!

github-actions[bot] avatar Feb 20 '25 20:02 github-actions[bot]

AmplifyOutputsData is marked internal because it is not considered stable. We may at times, change the internal structure, variable names, etc within AmplifyOutputsData. If we were to make this public, any changes would require a major release.

There are major release of AWS Amplify Gen 2, and at this point the configuration shouldn't be something that could change in the next major release.

We are potentially already have an issue even with this internal approach because some changes to configuration may have a significant undesirable side-effects.

Here is a scenario.

Presume that AWS Amplify Gen 2 developers changed the Amplify.AmplifyOutputsData.Storage struct from a struct having two string properties (awsRegion & bucketName) into an array of those properties. Such changes will have undesirable impact to application developers, such as myself, when they upgrade to that AWS Amplify Gen 2 release.

Application developers would be required to download a revised the amplify_outputs.json. Even though their AWS Amplify backend doesn't change and using the revised configuration layout doesn't benefit their applications.

Additionally, in my scenario the application is deployed in many time, thus introducing complexities during deployment of the application that is prone to errors since each instance will require changing the amplify_outputs.json on each device.

My advice for the AWS Amplify Gen 2 developers, use one the many paradigms that allow supporting different configuration revisions across releases. Eventually, the AWS Amplify Gen 2 developers will declare that earlier revisions are deprecated where application developers are required to downloading the appropriate amplify_outputs.json.

Android has a way to pass a json string, instead of a file, so this may be something that Swift can look into adding as well. You could store the json string however you would like.

What I've done is to construct extract the properties from a secure source and placed those into a JSON representation into a Data variable then pass that variable.

However, I would like to assure you that no data within the amplify_outputs.json file is considered private. There should be no need to encrypt these values.

I'll encourage you to discuss amongst your peers about the merits preventing individuals from accessing such information in the first place.

I've had to deal with domain where skilled bad actors with sufficient details such as endpoints & AWS S3 bucket details were enough for them to cause mischief.

I'll go ahead and mark this as a feature request.

Thanks, I do appreciate that as well other persons (if not now, sometime in the future). ;-)

sbelbin avatar Feb 20 '25 23:02 sbelbin

@sbelbin I did not mean to indicate that amplify_outputs.json is not under a public contract. That contract is set. How we choose to parse that json and convert it into structs/classes that we use internally within Amplify is what is subject to change. Since it is not public, any changes made within would not be breaking.

tylerjroach avatar Feb 20 '25 23:02 tylerjroach

@sbelbin Would you be able to share the categories that you are using? May be I can help you construct with already public contracts.

harsh62 avatar Jun 17 '25 03:06 harsh62

Presently what I've done is to replicating the AmplifyOutputsData struct & removed the @_spi(InternalAmplifyConfiguration) directives on all nested structures.

Those that the app needs:

  • Auth
  • OAuth
  • UsernameAttributes
  • Storage
  • DataCategory
  • AWSRegion

At some point, our app might require Notifications.

I appreciate taking time to reach out and looking into this matter.

Regards, Steven

P.S. Just some observations:

Given that amplify_outputs.json JSON document app developers can access since their app is to provide that file to Amplify, I don't see benefits to specify @_spi(InternalAmplifyConfiguration) directives on these properties.

I understand that maintainers of this open source that for some properties aren't finalized and want a mechanism so that app developers from relying on such properties. However, it doesn't prevent them from finding solutions that add code maintenance whereas the better code maintenance is accessing AmplifyOutputsData & its properties.

For maintainers of Amplify, my suggest is to declare which properties that are expected from this point onwards. As other properties to declare or document them as experimental or obsolete so that app developers are acknowledge that relying on these properties that they shall accept breaking changes down the road when such a property is removed or changes (name or type).

neuroservo avatar Jun 18 '25 18:06 neuroservo

Since you need to save your config in an encrypted manner and then load them at runtime, you can configure amplify as described below by injecting your values after fetching it from your store and then decrypting them.

Spec Auth: https://docs.amplify.aws/gen1/swift/prev/build-a-backend/auth/existing-resources/ Spec API: https://docs.amplify.aws/gen1/swift/prev/build-a-backend/restapi/existing-resources/

Example:

func addAmplify() throws {
    // Add plugins
    try Amplify.add(plugin: AWSCognitoAuthPlugin())
    try Amplify.add(plugin: AWSAPIPlugin())
    try Amplify.add(plugin: AWSS3StoragePlugin())
    
    let configuration = AmplifyConfiguration(
        auth: AuthCategoryConfiguration(
            plugins: [
                "awsCognitoAuthPlugin": [
                    "CognitoUserPool": [
                        "Default": [
                            "PoolId": "[USER_POOL_ID]",
                            "AppClientId": "[APP_CLIENT_ID]",
                            "Region": "[REGION]"
                        ]
                    ],
                    "Auth": [
                        "Default": [
                            "authenticationFlowType": "USER_SRP_AUTH"
                        ]
                    ]
                ]
            ]
        ),
        api: APICategoryConfiguration(
            plugins: [
                "awsAPIPlugin": [
                    "default": [
                        "endpointType": "REST",
                        "endpoint": "[API_ENDPOINT]",
                        "region": "[REGION]",
                        "authorizationType": "AMAZON_COGNITO_USER_POOLS"
                    ]
                ]
            ]
        ),
        storage: StorageCategoryConfiguration(
            plugins: [
                "awsS3StoragePlugin": [
                    "bucket": "[BUCKET_NAME]",
                    "region": "[REGION]"
                ]
            ]
        )
    )
    try Amplify.configure(configuration)
}

harsh62 avatar Jun 20 '25 00:06 harsh62

We've take an approach such that our app extracts sensitive fields from a secure store on the device.

One concern with your suggestion of replacing placeholders within the amplify_outputs.json is tylerjroach's response earlier in this thread stating:

AmplifyOutputsData is marked internal because it is not considered stable. We may at times, change the internal structure, variable names, etc within AmplifyOutputsData. If we were to make this public, any changes would require a major release. See: https://github.com/aws-amplify/amplify-swift/issues/3960#issuecomment-2672652021

Based on this statement of the AmplifyOutputsData hasn't been finalized (stable), then be extension the underlying JSON document of amplify_outputs.json isn't finalized as well.

Hence, a maintainer the Amplify project in future version can alter the JSON document such as rename, move or change the type of fields at their discretion.

Relying on your suggestion of replacing placeholders within the JSON document leads to a runtime failure in apps. This isn't an approach that I'm willing (can) proceed with since it's indeterministic.

Instead I suggest to those that maintain Amplify review & discuss about AmplifyOutputsDatadata structure to finalize it such that app developers can feed AmplifyOutputsDatadata structure without concern of runtime errors.

Should in later Amplify versions should AmplifyOutputsDatadata structure be altered in a manner as described above results in compilation error. Hence, when an app developers adapts the app's dependency to that Amplify version they'll encounter compilation errors.

They'll opt change the app's dependency to use an Amplify version that complies or use to use a later version which is a breaking change and they're aware that to use that version requires code changes.

Regards, Steven

sbelbin avatar Jun 20 '25 13:06 sbelbin

Hi @sbelbin I just want to clarify this concern.

Based on this statement of the AmplifyOutputsData hasn't been finalized (stable), then be extension the underlying JSON document of amplify_outputs.json isn't finalized as well.

This statement is not accurate. amplify_outputs.json is considered stable. AmplifyOutputsData is the Swift representation of the json file. The deserialization of the json structure into the Swift AmplifyOutputsData is what we could change given the internal marking.

tylerjroach avatar Jun 20 '25 15:06 tylerjroach

The v1 schema for amplify_outputs can be found here: https://github.com/aws-amplify/amplify-backend/blob/main/packages/client-config/src/client-config-schema/schema_v1.json. It is used for all of our client libraries and must remain stable for compatibility.

Just for a basic example. amplify_outputs values use underscores (user_pool_id), while we convert these values to camel casing (userPoolId). If we decided to change the deserialized variable names further, this would be a breaking change if AmplifyOutputsData were public, however, no changes were made with amplify_outputs.json.

I understand your use case, and we have discussed better typesafe configurability of the plugins internally without json based configuration. One of us will update this ticket if any changes are made that enable configuration without relying on the json structure.

tylerjroach avatar Jun 20 '25 15:06 tylerjroach

In addition to what Tyler just explained:

try Amplify.configure(configuration) uses AmplifyConfiguration which is public and can be used to embed config at runtime. I can be very certain that the config definition is not going to change without a major version bump. We are very strict in making sure our existing customers are not broken in any way.

harsh62 avatar Jun 20 '25 16:06 harsh62

Closing in favor of https://github.com/aws-amplify/amplify-swift/issues/4030

harsh62 avatar Jul 31 '25 18:07 harsh62

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

github-actions[bot] avatar Jul 31 '25 18:07 github-actions[bot]