r = /\A(\+(?:\d{3} ){2}(?:\d{2} ){2}99)\s+([A-Z]{3})\s+\((\d{9})\)/
phone, mode, device_id = "+123 456 78 90 99 SMS (185999922)".match(r)&.captures
#=> ["+123 456 78 90 99", "SMS", "185999922"]
phone, mode, device_id = "+12 456 78 90 99 SMS (185999922)".match(r)&.captures
#=> nil ^
phone, mode, device_id = "+123 456 78 90 99 SMSS (185999922)".match(r)&.captures
#=> nil ^
phone, mode, device_id = "+123 456 78 90 98 SMS (185999922)".match(r)&.captures
#=> nil ^
& is the safe navigation operator.
We can write the regular expression in free-spacing mode to make it self-documenting.
/
\A # match beginning of string
( # begin capture group 1
\+ # match '+'
(?:\d{3}[ ]) # match 3 digits followed by a space in a non-capture group
{2} # execute non-capture group twice
(?:\d{2}[ ]) # match 2 digits followed by a space in a non-capture group
{2} # execute non-capture group twice
99 # match '99'
) # end capture group 1
\s+ # match one or more whitespaces
([A-Z]{3}) # match three capital letters, save to capture group 2
\s+\( # match one or more whitespaces followed by '('
(\d{9}) # match nine digits, save to capture group 3
\) # match ')'
/x # free-spacing regex definition mode
In free-spacing mode spaces and comments are removed before the expression is parsed. It therefore is necessary to protect spaces that are part of the expression. I've done this by putting two in a character class ([ ]). There are various other ways to do that, one being to escape the space characters ((?:\d{3}\ )).