3

Ok, I am building an iMessage app and to transfer data back and forth I have to use URLQueryItems. I am working with an SKScene and need to transfer Ints, CGPoints, images, etc. Reading Apple's documentation and my own attempts it seems like you can only store strings in URLQueryItems.

As this us the only way to pass data back and forth, is there a (better) way to store other types of data? Currently I have been doing this:

 func composeMessage(theScene: GameScene) {
        let conversation = activeConversation
        let session = conversation?.selectedMessage?.session ?? MSSession()

        let layout = MSMessageTemplateLayout()
        layout.caption = "Hello world!"

        let message = MSMessage(session: session)
        message.layout = layout
        message.summaryText = "Sent Hello World message"

        var components = URLComponents()
        let queryItem = URLQueryItem(name: "score",value: theScene.score.description)
       components.queryItems = [queryItem] //array of queryitems
        message.url = components.url!

        print("SENT:",message.url?.query)

        conversation?.insert(message, completionHandler: nil)
    }

Then on the flip side I have to convert this string back to an Int again. Doing this with CGPoints will be inefficient.. how would one pass something like a CGPoint in a URLQueryItem? Any other way than storing the x and y values as strings?

EDIT: This is how I have been receiving data from the other person and putting into their scene:

override func willBecomeActive(with conversation: MSConversation) {
        // Called when the extension is about to move from the inactive to active state.
        // This will happen when the extension is about to present UI.

        // Use this method to configure the extension and restore previously stored state.

        let val = conversation.selectedMessage?.url?.query?.description
         print("GOT IT ", val)

        if(val != nil)
        {
            scene.testTxt = val!
        }
    }
Fahim
  • 3,466
  • 1
  • 12
  • 18
blue
  • 7,175
  • 16
  • 81
  • 179

2 Answers2

3

As you discovered, to pass data via URLQueryItem, you do have to convert everything to Strings since the information is supposed to be represented as a URL after all :) For CGPoint information, you can break the x and y values apart and send them as two separate Ints converted to String. Or, you can send it as a single String value in the form of "10,5" where 10 is the x and 5 is the y value but at the other end you would need to split the value on a comma first and then convert the resulting values back to Ints, something like this (at the other end):

let arr = cgPointValue.components(separatedBy:",")
let x = Int(arr[0])
let y = Int(arr[1])

For other types of data, you'd have to follow a similar tactic where you convert the values to String in some fashion. For images, if you have the image in your resources, you should be able to get away with passing just the name or an identifying number. For external images, a URL (or part of one if the images all come from the same server) should work. Otherwise, you might have to look at base64 encoding the image data or something if you use URLQueryItem but if you come to that point, you might want to look at what you are trying to achieve and if perhaps there is a better way to do it since large images could result in a lot of data being sent and I'm not sure if iMessage apps even support that. So you might want to look into limitations in the iMessage app data passing as well.

Hope this helps :)

Fahim
  • 3,466
  • 1
  • 12
  • 18
  • Thank you, you're great. one more question, just to see if Ive been doing this correctly (see edit above) Ive been receiving these queried urls in willBecomeActive - is this the right place to do this? – blue Apr 11 '17 at 01:33
  • Is there a better way to separate the multiple queries? – blue Apr 11 '17 at 01:34
  • Regarding the question about `willBecomeActive`, it has been a while since I've done an iMessage app, but I believe that is the correct place to handle an incoming message since that method is specifically meant to handle incoming conversations and for the app to take action. Regarding your second question, what do you mean by "separate multiple queries"? Do you mean separate multiple data items in one message? Or do you mean handle multiple conversations coming in at once? – Fahim Apr 11 '17 at 01:42
  • I am right now experimenting with sending 2 url queries - How do you break up the url into 2 or multiple different queries by key? – blue Apr 11 '17 at 01:55
  • Getting errors with https://developer.apple.com/reference/messages/msmessage/1649739-url – blue Apr 11 '17 at 02:02
  • Breaking up the message into two URLs so that you can send more data might not work - or it might, but I would think that might get a bit complicated. You might want to re-think that one :) With regards to the errors you mentioned in the second comment, what are the errors? Edit your question to include the information if necessary since the comments can be a bit limiting to post stuff ... – Fahim Apr 11 '17 at 02:07
  • Ok - I posted a new question for this new topic to make things more clear http://stackoverflow.com/questions/43335667/how-to-pass-and-get-multiple-urlqueryitems-in-swift – blue Apr 11 '17 at 02:12
  • Thanks for your help again! – blue Apr 11 '17 at 02:13
0

You can use iMessageDataKit library for storing key-value pairs in your MSMessage objects. It makes setting and getting data really easy and straightforward like:

let message: MSMessage = MSMessage()

message.md.set(value: 7, forKey: "moveCount")
message.md.set(value: "john", forKey: "username")
message.md.set(values: [15.2, 70.1], forKey: "startPoint")
message.md.set(values: [20, 20], forKey: "boxSize")

if let moveCount = message.md.integer(forKey: "moveCount") {
    print(moveCount)
}
if let username = message.md.string(forKey: "username") {
    print(username)
}
if let startPoint = message.md.values(forKey: "startPoint") {
    print("x: \(startPoint[0])")
    print("y: \(startPoint[1])")
}
if let boxSize = message.md.values(forKey: "boxSize") {
  let size = CGSize(width: CGFloat(boxSize[0] as? Float ?? 0),
                    height: CGFloat(boxSize[1] as? Float ?? 0))
  print("box size: \(size)")
}

(Disclaimer: I'm the author of iMessageDataKit)

Ahmet Ardal
  • 1,162
  • 1
  • 11
  • 12