imessage extension app group conversation send fails on first run

858 views Asked by At

I have an imessage extension app that works fine except on the first send to a group.
iOS 14.4 multiple devices 8, 8plus, 10... Xcode 12.4

The code goes straight from the send closure (success) to didResignActive. The app is supposed to stay active. There is no dismiss called anywhere. I've debugged w/ attached device stepped through the code, and it goes straight from the log line to didResignActive.

If I launch the app again on the same group it works fine. The app only fails if I start a new group conversation, then click on the app in tray, and send is called.

note: this only happens to group sends, and only on the first time a group is created.

       thisConversation.send(message) { error in
            if let error = error {
                os_log("submitMessage(%@): initial send error: %@", log: .default, type: .debug, type, error.localizedDescription)
            } else {
                os_log("submitMessage(%@): initial send success!", log: .default, type: .debug, type)
            }
        }
1

There are 1 answers

0
ekd On

Edit: Just an update... I was told to file a bug report for this (which I did).

I also supplied a video which shows how to reproduce the bug.

This is the message sent back from support...

Hi,

Thank you contacting Apple Developer Technical Support (DTS).

There is no workaround DTS can provide for Feedback ID #FB9049862; it is still under investigation. Please continue to track the problem via the bug report. ...

I was able to create a test app that reproduces this problem, with what I think is the minimal amount of code to demonstrate. Hope this invites a solution!

//
//  MessagesViewController.swift
//  testGroupSend MessagesExtension
//

import UIKit
import Messages
import os

class MessagesViewController: MSMessagesAppViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    // MARK: - Conversation Handling
    func composeSelectionMsg(on conversation: MSConversation, in
        session: MSSession) -> MSMessage {
         
        let layout = MSMessageTemplateLayout()
        layout.caption = "caption..."

        let message = MSMessage(session: session)
        message.layout = layout
        message.summaryText = "summary text..."
        
        var components = URLComponents()
        var queryItems = [URLQueryItem]()
        
        queryItems.append(URLQueryItem(name: "MessageType1", value: "msgType"))
        queryItems.append(URLQueryItem(name: "Encode-Name", value: "Encode-Value"))


        components.queryItems = queryItems
        message.url = components.url!
        
        return message
    }

    func submitMessage() {
        guard let conversation = activeConversation else {
            os_log("submitMessage(): guard on conversation falied!", log: .default, type: .debug)
            return
        }
        var session : MSSession
        if let tSess = conversation.selectedMessage?.session {
            session = tSess
            os_log("submitMessage() got a session!...", log: .default, type: .debug)
        } else {
            os_log("###### submitMessage() did NOT get a session, creating new MSSession() #####", log: .default, type: .debug)
            session = MSSession()
        }
        var message: MSMessage
         
        message = composeSelectionMsg(on: conversation, in: session)
            
            //  conversation.insert(message) { error in
        conversation.send(message) { error in
                if let error = error {
                    os_log("submitMessage(): initial send error: %@", log: .default, type: .debug, error.localizedDescription)
                } else {
                    os_log("submitMessage(): initial send success!", log: .default, type: .debug)
                }
            }
    }

    fileprivate func loadContentView() {
        os_log("loadContentView()...", log: .default, type: .debug)
       let childViewCtrl = ContentViewHostController()
        
        childViewCtrl.delegate = self
        childViewCtrl.view.layoutIfNeeded() // avoids snapshot warning?
        
        if let window = self.view.window {
            childViewCtrl.myWindow = window
            window.rootViewController = childViewCtrl
        }
    }
    
    override func willBecomeActive(with conversation: MSConversation) {
        loadContentView()
    }
    
    override func didResignActive(with conversation: MSConversation) {
        os_log("didResignActive()...", log: .default, type: .debug)

    }
}
//
//
import SwiftUI
import Messages
import os

final class ContentViewHostController: UIHostingController<ContentView> {
    weak var delegate: ContentViewHostControllerDelegate?
    weak var myWindow: UIWindow?

    init() {
        
        super.init(rootView: ContentView())
        rootView.submitMessage = submitMessage
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder, rootView: ContentView())
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    
    func submitMessage() {
        os_log("ContentViewHostController::submitMessage(): submit message...", log: .default, type: .debug)
        delegate?.contentViewHostControllerSubmitMessage(self)
    }
}

struct ContentView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    var submitMessage: (() -> Void)?
       
    var body: some View {
        VStack {
            VStack {
                HStack {
                    Button(action: { self.cancel() } ) { Image(systemName: "chevron.left") }
                        .padding()
                    
                    Spacer()
               
                    Button(action: { self.submit() } ) {
                        Text("Send...")
                    }
                    .padding()
                    
                } // HStack
            } // VStack
        } // outside most VStack

    } // body
  
    private func cancel() {
        presentationMode.wrappedValue.dismiss()
    }
    
    private func submit() {
        submitMessage!()
        presentationMode.wrappedValue.dismiss()
    }
} // ContentView


//
//

import SwiftUI
import Messages
import os

extension MessagesViewController: ContentViewHostControllerDelegate {
        // MARK: - ContenHost delegate

        func contentViewHostControllerSubmitMessage(_ controller: ContentViewHostController) {
            os_log("delegateSubmitMessage:...")
            
            submitMessage()
        }

}

//
//
import SwiftUI
import Messages

protocol ContentViewHostControllerDelegate: class  {
    func contentViewHostControllerSubmitMessage( _ controller: ContentViewHostController )
}