aws-amplify / amplify-swift

A declarative library for application development using cloud services.
Apache License 2.0
447 stars 195 forks source link

When executing Amplify.Storage.uploadFile() on an iOS share extension, the error Error Domain=NSURLErrorDomain Code=-995 "(null)" occurs. #3340

Closed Aoi-Takahashi closed 1 day ago

Aoi-Takahashi commented 11 months ago

Describe the bug

Hi!,Firstly, I would like to express my gratitude for this wonderful framework and all the advice from everyone.

Currently, we are developing a mobile app for both iOS and Android using React Native. To share PDFs with our app in development from external sources, we are considering using the iOS Share Extension to directly upload shared PDFs to our amplify S3 setup for mobile app use. However, when we execute Amplify.Storage.uploadFile() inside the ShareViewController UI's didSelectPost() provided by the Share Extension, the error Error Domain=NSURLErrorDomain Code=-995 "(null)"occurs. Below is the minimum code snippet that I can provide.

import UIKit
import Social
import Amplify
import AWSAPIPlugin
import AWSCognitoAuthPlugin
import AWSS3StoragePlugin

class ShareViewController: SLComposeServiceViewController {
  var typeIdentifier = "com.adobe.pdf"
  override func viewDidLoad() {
    super.viewDidLoad()
    do {
      Amplify.Logging.logLevel = .verbose
      try Amplify.add(plugin: AWSCognitoAuthPlugin())
      try Amplify.add(plugin: AWSAPIPlugin())
      try Amplify.add(plugin: AWSS3StoragePlugin())
      try Amplify.configure()
      print("Amplify configured with auth plugin")
    } catch {
      assertionFailure("Error initializing Amplify: \(error)")
    }
  }

  override func isContentValid() -> Bool {
    // Do validation of contentText and/or NSExtensionContext attachments here
    return true
  }

  override func didSelectPost() {
    // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
    // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
    if let item = extensionContext?.inputItems.first as? NSExtensionItem {
      if let itemProvider = item.attachments?.first {
        if itemProvider.hasItemConformingToTypeIdentifier(typeIdentifier) {
          itemProvider.loadItem(forTypeIdentifier:typeIdentifier, options: nil) { [weak self] (data, error) in
            guard let self = self else { return }

            if let fileURL = data as? URL {
              Task {
                do {
                  let isSignedIn = try await Amplify.Auth.fetchAuthSession().isSignedIn
                  if !isSignedIn {
                    print("User is not signed in!")
                    self.extensionContext?.cancelRequest(withError: NSError(domain: "Error", code: 401, userInfo: ["message": "User not authenticated"]))
                  }
                  let fileNameKey = "myDocument.pdf"
                  let options = StorageUploadFileRequest.Options(accessLevel: .private)
                  let uploadTask = Amplify.Storage.uploadFile(
                    key: fileNameKey,
                    local: fileURL,
                    options: options
                  )
                  let data = try await uploadTask.value
                  print("Completed: \(data)")
                  self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
                } catch {
                  print("Error: \(error)")
                  self.extensionContext?.cancelRequest(withError: NSError(domain: "Error", code: 400, userInfo: ["message": "Failed to upload PDF"]))
                }
              }
            }
          }
        }
      }
    }
  }
  override func configurationItems() -> [Any]! {
    // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
    return []
  }

}

Steps To Reproduce

1. Select `amplify-swift` in Swift PM, and add the following:
   - AWSAPIPlugin
   - AWSCognitoAuthPlugin
   - AWSPluginsCore
   - AWSS3StoragePlugin
   - Amplify
2. Execute `Amplify.add()` with the above plugins in `ShareExtension.swift`.
3. Execute `Amplify.configure()`.
4. Retrieve the PDF to be shared within `didSelectPost()` of the ShareViewController UI.
5. Execute `Amplify.Storage.uploadFile()` within `didSelectPost()` of the ShareViewController UI.
6. Confirm that the error `Error Domain=NSURLErrorDomain Code=-995 "(null)"` occurs and the PDF is not successfully uploaded to S3.

Expected behavior

The PDF selected in ShareExtension is successfully uploaded to S3.

Amplify Framework Version

2.21.3

Amplify Categories

Storage

Dependency manager

Swift PM

Swift version

5.0

CLI version

No Responce

Xcode version

Version 15.0

Relevant log output

<details>
<summary>Log Messages</summary>

ResumableUploadState: cannot initWithOriginalRequest with nil request
Started upload [1]
Resuming storage transfer task: 1
API MISUSE: Resuming an NSURLSessionTask with nil URL.
Task <C078E9F6-AC28-45F2-9D1B-96E8C6570F17>.<1> finished with error [18,446,744,073,709,550,621] Error Domain=NSURLErrorDomain Code=-995 "(null)" UserInfo={_NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundUploadTask <C078E9F6-AC28-45F2-9D1B-96E8C6570F17>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <C078E9F6-AC28-45F2-9D1B-96E8C6570F17>.<1>}
[URLSession] Session task did complete with error: 1 [Error Domain=NSURLErrorDomain Code=-995 "(null)" UserInfo={_NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundUploadTask <C078E9F6-AC28-45F2-9D1B-96E8C6570F17>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <C078E9F6-AC28-45F2-9D1B-96E8C6570F17>.<1>}]```
</details>

Is this a regression?

Yes

Regression additional context

No Responce

Platforms

iOS

OS Version

Main App iOS12.4 / ShareExtention iOS17.0

Device

iPhone15 Pro

Specific to simulators

No Responce

Additional context

I have conducted my own investigation regarding this issue and confirmed that the following operations work as expected:

Amplify/Core/Support/AmplifyTask+OperationTaskAdapters.swift


public var value: Success {
        get async throws {
            try await childTask.value
        }
    }
github-actions[bot] commented 11 months ago

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!

thisisabhash commented 11 months ago

Hello @Aoi-Takahashi , App Extensions are currently not supported with Amplify. The error you're seeing is this one: https://developer.apple.com/documentation/foundation/1508628-url_loading_system_error_codes/nsurlerrorbackgroundsessionrequiressharedcontainer

We are marking this as a feature request and update this thread we have an update.

Aoi-Takahashi commented 11 months ago

Hello @Aoi-Takahashi , App Extensions are currently not supported with Amplify. The error you're seeing is this one: https://developer.apple.com/documentation/foundation/1508628-url_loading_system_error_codes/nsurlerrorbackgroundsessionrequiressharedcontainer

We are marking this as a feature request and update this thread we have an update.

@thisisabhash Oh...I see. Actually, I had a inkling of this result (that Amplify does not support File Up/Down on the Share extension).... I understand that you are considering this as a feature request, but could you please tell us about the following?

harsh62 commented 11 months ago

Related: https://github.com/aws-amplify/amplify-swift/issues/2508

thisisabhash commented 11 months ago

Hello @Aoi-Takahashi You may want to look at this thread here - https://stackoverflow.com/questions/25438709/afnetworking-background-session-configuration-for-ios-8-extension

NSURLSession expects the App Extension to share the same container identifier as the host app. Currently, it is not configurable while using Amplify Storage APIs. However, you may work around this by using Escape Hatch. https://docs.amplify.aws/lib/storage/escapehatch/q/platform/ios/

This will give you access to the S3Client where you can use your custom URLSession object with shared container identifier and call methods on the S3Client directly.

Aoi-Takahashi commented 10 months ago

Hello @Aoi-Takahashi You may want to look at this thread here - https://stackoverflow.com/questions/25438709/afnetworking-background-session-configuration-for-ios-8-extension

NSURLSession expects the App Extension to share the same container identifier as the host app. Currently, it is not configurable while using Amplify Storage APIs. However, you may work around this by using Escape Hatch. https://docs.amplify.aws/lib/storage/escapehatch/q/platform/ios/

This will give you access to the S3Client where you can use your custom URLSession object with shared container identifier and call methods on the S3Client directly.

@thisisabhash @harsh62  I am currently working on a solution to this problem based on your response, but I can't think of a specific technique based on the URL you provided, I would like to know if there are any other specific tips or approaches to solving this problem: ....

github-actions[bot] commented 1 day ago

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.