diff --git a/README.md b/README.md index 7f92204..afb64e1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,139 @@ -## My Project +# SwiftChat - A Sample Cross-platform AI Chat App -TODO: Fill this README out! +SwiftChat is a fast and responsive AI chat application built with React Native and Amazon Bedrock. Featuring a +minimalist design philosophy and strong privacy protection, it offers real-time streaming conversations and AI image +generation across Android, iOS, and macOS platforms. -Be sure to: +![](images/promo.png) -* Change the title in this README -* Edit your repository description on GitHub +**Key Features:** + +- Real-time streaming chat with AI +- AI image generation +- Cross-platform support (Android, iOS, macOS) +- Tablet-optimized for iPad and Android tablets +- Fast launch and responsive performance +- Multiple AI model support and switching +- Built with React Native +- Powered by Amazon Bedrock + +## Architecture + +![](/images/architecture.png) + +App Runner can provide lower latency for API responses, while you can also replace App Runner with Lambda and Lambda +Function URL to further reduce costs. For details, please refer to +this [example](https://github.com/awslabs/aws-lambda-web-adapter/tree/main/examples/fastapi-response-streaming). + +## Getting Started + +### Step 1: Set up your API Key + +1. Click [Parameter Store](https://console.aws.amazon.com/systems-manager/parameters/) to open your AWS Console. +2. Check whether you are in the [supported region](#supported-region), then click on the "Create parameter" button. +3. Name: Enter a descriptive name for your parameter (e.g., "SwiftChatAPIKey"). +4. Tier: Select **Standard**. +5. Type: Select **SecureString**. +6. Value: Any string without spaces (This value is your API Key which should fill in your App's Settings page). +7. Click "Create parameter". +8. Make a note of the parameter name you used (e.g., "SwiftChatAPIKey"). You'll need this in the next step. + +### Step 2: Deploy stack and get your API URL + +1. Click the following button to launch the CloudFormation Stack in the same region with your API Key just created. + - App Runner + + [![Launch Stack](images/launch-stack.png)](https://console.aws.amazon.com/cloudformation/home#/stacks/create/template?stackName=SwiftChatAPI&templateURL=https://aws-gcr-solutions.s3.amazonaws.com/swift-chat/latest/SwiftChatAppRunner.template) + + - Lambda (You need to config your Lambda Function URL manually) + + [![Launch Stack](images/launch-stack.png)](https://console.aws.amazon.com/cloudformation/home#/stacks/create/template?stackName=SwiftChatAPI&templateURL=https://aws-gcr-solutions.s3.amazonaws.com/swift-chat/latest/SwiftChatLambda.template) + +2. Click **Next**. +3. On the "Specify stack details" page, provide the following information: + - Fill the `ApiKeyParam` with the parameter name you used for storing the API key (e.g., "SwiftChatAPIKey"). + - For App Runner, choose an `InstanceTypeParam` based on your needs: select lower Memory/CPU for cost savings, or + higher for better + performance. +4. Click **Next**, Keep the "Configure stack options" page as default, Read the Capabilities and Check the "I + acknowledge that AWS CloudFormation might create IAM resources" checkbox at the bottom. +5. Click **Next**, In the "Review and create" Review your configuration. +6. Click **Submit**. + +Wait about 3-5 minutes for the deployment to finish. + +For App Runner, click the CloudFormation stack and go to Outputs tab, you can find the **API URL** +(which looks like https://xxx.xxx.awsapprunner.com) + +For Lambda, follow these steps to get your **API URL** by enabling Lambda Function URL manually: + +1. Click the CloudFormation stack and go to **Resources** tab +2. Expand APIHandler under Physical ID, click the Lambda link to open Lambda page +3. In Lambda page, go to **Configuration** -> **Function URL** to enable Lambda Function URL with **RESPONSE_STREAM** + invoke mode +4. You'll find your Lambda Function URL like `https://xxx.lambda-url.xxx.on.aws` - use this as your **API URL** + +### Step 3: Download the app and setup with API URL and API Key + +1. Download the App + - Android App [Download](https://github.com/aws-samples/swift-chat/releases/download/v1.5.0/SwiftChat.apk) + - macOS App [Download](https://github.com/aws-samples/swift-chat/releases/download/v1.5.0/SwiftChat.dmg) + - iOS (Currently we do not provide the iOS version, you can build it locally with Xcode) + +2. Launch your App, Click the menu button in the top left to open the drawer page, click **Settings** in the bottom of + the drawer. +3. Paste the API URL and API Key then select the Region. +4. You can change the default text and image model, and **make sure you have enabled these models in your AWS console**. +5. Click the top right finish icon to save your configuration and start your chat. + +Congratulations! Your SwiftChat App is ready to use 🎉 + +### Supported Region + +- US East (N. Virginia): us-east-1 +- US West (Oregon): us-west-2 +- Asia Pacific (Mumbai): ap-south-1 +- Asia Pacific (Singapore): ap-southeast-1 +- Asia Pacific (Sydney): ap-southeast-2 +- Asia Pacific (Tokyo): ap-northeast-1 +- Canada (Central): ca-central-1 +- Europe (Frankfurt): eu-central-1 +- Europe (London): eu-west-2 +- Europe (Paris): eu-west-3 +- South America (São Paulo): sa-east-1 + +## Why Swift? + +## App Privacy & Security + +- Encrypted API key storage +- Minimal permission requirements +- Local data storage only +- No user behavior tracking +- No data collection +- Privacy-first approach + +## Build and development + +Firstly, clone this repo and run `npm i` to download the dependencies. + +### Build for Android + +```bash +npm run start && npm run android +``` + +### Build for iOS + +```bash +npm run start && npm run ios +``` + +### Build for macOS + +1. Modify as `isMac = true` in `/src/App.tsx` and execute `npm run start`. +2. Double click `ios/SwiftChat.xcworkspace` to open the project in your Xcode. +3. Change the build destination to `My Mac (Mac Catalyst)` then click the ▶ Run button. ## Security diff --git a/android/app/build.gradle b/android/app/build.gradle index 3d81498..dccdc44 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "com.aws.swiftchat" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7 - versionName "1.4.0" + versionCode 8 + versionName "1.5.0" ndk { //noinspection ChromeOsAbiSupport abiFilters 'arm64-v8a' diff --git a/images/architecture.png b/images/architecture.png new file mode 100644 index 0000000..4b27eeb Binary files /dev/null and b/images/architecture.png differ diff --git a/images/launch-stack.png b/images/launch-stack.png new file mode 100644 index 0000000..9d490ae Binary files /dev/null and b/images/launch-stack.png differ diff --git a/images/promo.png b/images/promo.png new file mode 100644 index 0000000..fe02db9 Binary files /dev/null and b/images/promo.png differ diff --git a/ios/SwiftChat.xcodeproj/project.pbxproj b/ios/SwiftChat.xcodeproj/project.pbxproj index b68e901..0f10cf4 100644 --- a/ios/SwiftChat.xcodeproj/project.pbxproj +++ b/ios/SwiftChat.xcodeproj/project.pbxproj @@ -485,7 +485,7 @@ CODE_SIGN_ENTITLEMENTS = SwiftChat/SwiftChat.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 8; DEVELOPMENT_TEAM = BUA6W9H7T3; ENABLE_BITCODE = NO; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; @@ -497,7 +497,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.5.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -526,7 +526,7 @@ CODE_SIGN_ENTITLEMENTS = SwiftChat/SwiftChat.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 8; DEVELOPMENT_TEAM = BUA6W9H7T3; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; INFOPLIST_FILE = SwiftChat/Info.plist; @@ -537,7 +537,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.5.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/package.json b/package.json index 0cc9059..18006bf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "swift-chat", "description": "Sample Bedrock Cross-platform App - SwiftChat", - "version": "1.4.0", + "version": "1.5.0", "private": true, "scripts": { "android": "react-native run-android", diff --git a/server/template/SwiftChatAppRunner.template b/server/template/SwiftChatAppRunner.template index 08ec578..545efeb 100644 --- a/server/template/SwiftChatAppRunner.template +++ b/server/template/SwiftChatAppRunner.template @@ -2,7 +2,8 @@ "Parameters": { "ApiKeyParam": { "Type": "String", - "Description": "API Key Parameter name for authentication" + "Description": "API Key Parameter name for authentication", + "MinLength": 1 }, "InstanceTypeParam": { "Type": "String", diff --git a/server/template/SwiftChatLambda.template b/server/template/SwiftChatLambda.template index 5aba174..992293e 100644 --- a/server/template/SwiftChatLambda.template +++ b/server/template/SwiftChatLambda.template @@ -2,7 +2,8 @@ "Parameters": { "ApiKeyParam": { "Type": "String", - "Description": "API Key Parameter name for authentication" + "Description": "API Key Parameter name for authentication", + "MinLength": 1 } }, "Resources": { @@ -146,43 +147,10 @@ "aws:cdk:path": "SwiftChatAPILambda/APIHandler/Resource" } }, - "APIHandlerFunctionUrlD2500C9A": { - "Type": "AWS::Lambda::Url", - "Properties": { - "AuthType": "NONE", - "InvokeMode": "RESPONSE_STREAM", - "TargetFunctionArn": { - "Fn::GetAtt": [ - "APIHandler68F11976", - "Arn" - ] - } - }, - "Metadata": { - "aws:cdk:path": "SwiftChatAPILambda/APIHandler/FunctionUrl/Resource" - } - }, - "APIHandlerinvokefunctionurl0B3BB28F": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunctionUrl", - "FunctionName": { - "Fn::GetAtt": [ - "APIHandler68F11976", - "Arn" - ] - }, - "FunctionUrlAuthType": "NONE", - "Principal": "*" - }, - "Metadata": { - "aws:cdk:path": "SwiftChatAPILambda/APIHandler/invoke-function-url" - } - }, "CDKMetadata": { "Type": "AWS::CDK::Metadata", "Properties": { - "Analytics": "v2:deflate64:H4sIAAAAAAAA/02Ny2rDQAxFvyX7sRInFLJuSiGrGJeuizJWi+KZUZBmGoLxvxdPH3R1Dpcr3S20Dy1sVnizxg9jE/gM00tGP7rDe+pQMVImdXizt4m8Qk9XMc6i90c0cowRpl4CLfXKTgL7e72uNruA8TwgTE/iR9JjxA96LslnlrTU/vxXXjUs+Q860shmLGmeXU8mRX1dO5V8Lbnu/ksPkgZevswuyUBwsfVnu4ftBnarizE3WlLmSNB/8wutZ8HgAQEAAA==" + "Analytics": "v2:deflate64:H4sIAAAAAAAA/02MwQqCQBRFv6X9+FIjaJ0RtBP7gHiOr3jqzIuZMZHBfw+VoNU5i3NvDtkxg3SHo0900yU91xDvAXWniqct0aGhQE7h6B+RtIOK3uI5iJvO6EkxGoiV9LTkK0vpWU/rerVZ9WjqBiFeRHfkbgZfdB2sDix2yX4+z6oiL4PT29mfF2IbXhtlpSFo/f6TnSBP4bBrPXPiBhvYEFQbv6y3L9bWAAAA" }, "Metadata": { "aws:cdk:path": "SwiftChatAPILambda/CDKMetadata/Default" @@ -190,25 +158,6 @@ "Condition": "CDKMetadataAvailable" } }, - "Outputs": { - "APIURL": { - "Description": "API URL for SwiftChat App", - "Value": { - "Fn::Join": [ - "", - [ - "https://", - { - "Fn::GetAtt": [ - "APIHandlerFunctionUrlD2500C9A", - "FunctionUrl" - ] - } - ] - ] - } - } - }, "Conditions": { "CDKMetadataAvailable": { "Fn::Or": [ diff --git a/src/storage/StorageUtils.ts b/src/storage/StorageUtils.ts index d1edac4..614cab0 100644 --- a/src/storage/StorageUtils.ts +++ b/src/storage/StorageUtils.ts @@ -234,11 +234,13 @@ export function getAllRegions() { 'us-west-2', 'us-east-1', 'ap-south-1', + 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'ca-central-1', 'eu-central-1', 'eu-west-2', + 'eu-west-3', 'sa-east-1', ]; }