Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Translation API & Download Language Sheet

Using Apple SwiftUI Translate library: when calling:

try await session.prepareTranslation()

the first time, the API's Language Download sheet does not show (or shows briefly and dismisses immediately) Even when the sheet doesn't show, the keyboard is lowered, as though the sheet would be appearing, but then the keyboard raises as though the sheet was dismissed.

The following Errors are printed in the console dozens of times; but on all subsequent executions, the API Language Download sheet shows as expected.

The trigger code is in a function which is executed when a Translate button is tapped.

Any ideas would be welcome!

Console Error on first execution only

LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=72, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
Attempt to map database failed: permission was denied. This attempt will not be retried.
Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=72, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=72, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
Attempt to map database failed: permission was denied. This attempt will not be retried.
Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=72, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=72, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
Attempt to map database failed: permission was denied. This attempt will not be retried.
Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=72, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}

Error returned from iconservicesagent image request: <ISBundleIdentifierIcon: 0x300df3c30> BundleID: (null) digest: 7749FEEE-F663-39B4-AD68-A18CFF762CCC - <ISImageDescriptor: 0x3033b26c0> - (64.00, 64.00)@2x v:4 l:5 a:0:0:0:0 t:() b:0 s:2 ps:0 digest: DF83A970-D4C9-3D90-BB7D-0BC21FC22E03 error: Error Domain=NSOSStatusErrorDomain Code=-609 "Client is disallowed from making such an icon request" UserInfo={NSLocalizedDescription=Client is disallowed from making such an icon request}
Error returned from iconservicesagent image request: <ISTypeIcon: 0x300d0fb70>,Type: com.apple.appprotection.badge.faceid - <ISImageDescriptor: 0x3033ad0e0> - (32.00, 32.00)@2x v:0 l:5 a:0:0:0:0 t:() b:0 s:2 ps:0 digest: 648D7A72-90CB-3858-9409-5C554BB43B8E error: Error Domain=NSOSStatusErrorDomain Code=-609 "Client is disallowed from making such an icon request" UserInfo={NSLocalizedDescription=Client is disallowed from making such an icon request}
Connection interrupted, finishing translation with error Error Domain=TranslationErrorDomain Code=14 "(null)"
Got response from extension with error: Error Domain=TranslationErrorDomain Code=14 "(null)"
Reported that remote UI finished but didn't get finished configuration, reporting the error as: Error Domain=TranslationErrorDomain Code=20 "(null)"
VS terminated with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}
Reported that remote UI finished but didn't get finished configuration, reporting the error as: Error Domain=TranslationErrorDomain Code=14 "(null)"
VS terminated with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}
VS terminated with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}
VS terminated with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}
VS terminated with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}

Trigger the Translation:

// check if we need to create a translation configuration
if configuration == nil {
    configuration = TranslationSession.Configuration.init(
        source: Locale.Language(identifier: sourceLanguageCode),
        target: Locale.Language(identifier: targetLanguageCode)
    )
} else {
    // or just update the target code then invalidate the config to re-trigger the refresh
of .translationTask()
    configuration?.source = Locale.Language(identifier: sourceLanguageCode)
    configuration?.target = Locale.Language(identifier: targetLanguageCode)
    configuration?.invalidate()
}

Prepare and check if Download sheet should be presented:

.translationTask(configuration) { session in
    do {
        // prepare translation & present API download sheet if lanugage download needed.
        try await session.prepareTranslation()
        
    } catch {
        print("Translate failed: \(error)")
    }
}

Progress Update:

An if-statement inside .translationTask(configuration){} seems to be causing the issue:.

The goal is using only 1 UI element, check if the language is downloaded. Only present the Download Sheet if needed; otherwise, perform the translation.

Presently looking for work-arounds.

Doesn't work (sheet auto-dismisses):

.translationTask(configuration) { session in
    Task { @MainActor in
        do {
            if performFullTranslate {

                // prepare
                try await session.prepareTranslation()
                
                // send inputText to Translation API
                let response = try await session.translate(textObject.inputText)
                
                // swap the inputText for the translation
                textObject.inputText = response.targetText
                
            } else {
                // just prepare the DL sheet
                try await session.prepareTranslation()
            }
            
        } catch {
            print("Translate failed: \(error)")
        }
    }
}

Works (presents sheet, but can't perform translation):

.translationTask(configuration) { session in
    Task { @MainActor in
        do {
            // just prepare the DL sheet
            try await session.prepareTranslation()
            
        } catch {
            print("Translate failed: \(error)")
        }
    }
}

Resolution

Incase anyone stumbles on this, here's our solution to "can't use if-statements in .translationTask(configuration)"

Originally I built a ViewModifier to switch between conditions (download only, or perform translation); but that didn't work for our use case. View Modifiers redraw the view causing TextEditors to lose their focus, which would not work for us.

I landed on making a second TranslationSession.Configuration one for checking if language is downloaded, one for performing the translation.

We check which to execute when the user taps the translate button:

Two Configurations

device owner is translating to
@State var configurationForDownloadCheck: TranslationSession.Configuration?
@State var configuration: TranslationSession.Configuration?

Check which to run

.onTapGesture {
    Task {
        // check if languages are downloaded and set control
var
        let languagesFullyDownloaded = await
translationLanguageIsDownloaded(from:
nativeLanguageCode!, to: guestLanguageCode!)
        performFullTranslate = languagesFullyDownloaded
        
        // decide if we need can translate or need to
download first.
        if performFullTranslate {
            // send for translation work
            performTranslation(from: nativeLanguageCode!, to:
guestLanguageCode!)
        } else {
            // send to check of Downloaded or not
            downloadLanguages(from: nativeLanguageCode!, to:
guestLanguageCode!)
        }
    }
}

View Modifier (works, but not for our needs)

@available(iOS 18.0, *)
struct TaskTranslationSwitchModifier: ViewModifier {
    @EnvironmentObject var textObject: TextObject
    @Binding var configuration: TranslationSession.Configuration?
    let performFullTranslate: Bool
    @Binding var lastTranslation: String

    // Callback for notifying about text changes
    var onTextChanged: ((String) -> Void)?
    
    func body(content: Content) -> some View {
        if performFullTranslate {
            content
                .translationTask(configuration) { session in
                    print("greg: performing full translate")
                    Task { @MainActor in
                        do {
                            // prepare
                            try await session.prepareTranslation()
                            
                            // send inputText to Translation API
                            let response = try await session.translate(textObject.inputText)
                            
                        } catch {
                            print("Translate failed: \(error)")
                        }
                    }
                }
        } else {
            content
                .translationTask(configuration) { session in
                    print("greg: performing DL sheet ONLY!")
                    Task { @MainActor in
                        do {
                            // just prepare the DL sheet
                            try await session.prepareTranslation()
                        } catch {
                            print("Translate failed: \(error)")
                        }
                    }
                }
        }
    }
}

Hello @OneManShy,

Only present the Download Sheet if needed;

That is the behavior of prepareTranslation(), "If the languages are already installed or are in the middle of downloading, the function returns without prompting the user."

I'm not completely sure what the issue you are facing is, could you provide a focused sample project that demonstrates the issue?

-- Greg

@DTS Engineer Thank you for the response. The attached code recreates the issue.

We discovered the root appears to be a variable state-change while the Download Sheet is presented causes the sheet to auto-dismiss with errors.

However, this error is only present on the first execution. Subsequent attempts do not auto-dismiss the sheet.

Please let us know if this description and sample code are clear enough. Thanks!

Example Code:

//
//  TranslateSheet.swift
//
//

import SwiftUI
import Translation


struct TranslateSheet: View {
    @State private var configuration: TranslationSession.Configuration?
    private let languageAvailability = LanguageAvailability()
    
    @State var text: String = "Sample Text to be Translated"
    @State var permitTranslation: Bool = true
    
    var body: some View {
        VStack (alignment: .center) {
            Text(text)
                .fontWeight(.black)
                .padding()
        
            Button("TAP to Translate English to Spanish & Show Download Sheet") {
                performTranslation(from: "en_US", to: "es_ES")
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                    permitTranslation = false
                }
            }
            .padding()

            Button("Permitted: \(permitTranslation)") {
                permitTranslation.toggle()
            }
        
            Group {
                Text("Make sure Spanish Language is not already downlaoded.")
                Text("First Launch: Permission is True. Tap Translate English to Spanish.")
                Text("Dispatch Queue simulates change of state from true to false while sheet is presented.")
                Text("When sheet dismisses, observe hundreds of lines of repeating errors in Xcode console.\n")
                Text("Also, setting the state back to true by tapping 'Permitted: false', then attempting translation again does not cause same auto-dismissal error!")

            }
            .font(.title3)
            
            Spacer()
        }
        .font(.title)
        .translationTask(configuration) { session in
            Task { @MainActor in
                do {
                    print("preparing...")
                    // Show Download Lanugage sheet from Translation API
                    try await session.prepareTranslation()
                    
                    print("executing translation")
                    let response = try await session.translate(text)
                    text = response.targetText
                    
                } catch {
                    print("Translate failed: \(error)")
                }
            }
        }
    }
    
    func performTranslation(from sourceLanguageCode: String, to targetLanguageCode: String) {
        print("excecuting performTranslation() - 830: sourceLanguageCode: \(sourceLanguageCode), targetLanguageCode: \(targetLanguageCode)")
        
        // check if we need to create a translation configuration
        if configuration == nil {
            configuration = TranslationSession.Configuration.init(
                source: Locale.Language(identifier: sourceLanguageCode),
                target: Locale.Language(identifier: targetLanguageCode)
            )
        } else {
            // or just update the target code then invalidate the config to re-trigger the refresh of .translationTask()
            configuration?.source = Locale.Language(identifier: sourceLanguageCode)
            configuration?.target = Locale.Language(identifier: targetLanguageCode)
            configuration?.invalidate()
        }
    }}
Translation API & Download Language Sheet
 
 
Q