11//
2- // Notifications .swift
2+ // BLEWriteManager .swift
33// InfiniLink
44//
5- // Created by Alex Emry on 8/8/21 .
5+ // Created by Liam Willey on 5/27/25 .
66//
77
88import Foundation
@@ -13,6 +13,7 @@ struct BLEWriteManager {
1313 let bleManager = BLEManager . shared
1414
1515 @AppStorage ( " watchNotifications " ) var watchNotifications = true
16+ @AppStorage ( " transliterationEnabled " ) var transliterationEnabled = true
1617
1718 func writeToMusicApp( message: String , characteristic: CBCharacteristic ) -> Void {
1819 guard bleManager. infiniTime != nil else { return }
@@ -47,24 +48,41 @@ struct BLEWriteManager {
4748
4849 func sendNotification( _ notif: AppNotification ) {
4950 guard bleManager. infiniTime != nil else { return }
50- guard let titleData = ( " " + notif. title + " \0 " ) . data ( using: . ascii) else { return }
51- guard let bodyData = ( notif. subtitle + " \0 " ) . data ( using: . ascii) else { return }
5251
53- var notification = titleData
52+ let title = transliterationEnabled ? notif. title. asciiSafe : notif. title
53+ let body = transliterationEnabled ? notif. subtitle. asciiSafe : notif. subtitle
5454
55- notification. append ( bodyData)
55+ // Convert strings to ASCII
56+ let titleData = ( title + " \0 " ) . data ( using: . ascii)
57+ let bodyData = ( body + " \0 " ) . data ( using: . ascii)
58+
59+ // Log if there was a failure when converting
60+ if titleData == nil {
61+ log ( " Failed to convert \( notif. title) to ASCII data " , caller: " BLEWriteManager " , target: . ble)
62+ }
63+ if bodyData == nil {
64+ log ( " Failed to convert \( notif. subtitle) to ASCII data " , caller: " BLEWriteManager " , target: . ble)
65+ }
66+
67+ // If either the strings couldn't be converted, don't send the notification
68+ if titleData == nil || bodyData == nil {
69+ return
70+ }
71+
72+ var notification = titleData!
73+ notification. append ( bodyData!)
5674
5775 if !notification. isEmpty && watchNotifications {
5876 bleManager. infiniTime. writeValue ( notification, for: bleManager. notifyCharacteristic, type: . withResponse)
59- log ( " Notification sent with title: \( notif . title) " , caller: " BLEWriteManager " , target: . ble)
77+ log ( " Notification sent with title: \( title) " , caller: " BLEWriteManager " , target: . ble)
6078 }
6179 }
6280
6381 func sendLostNotification( ) {
6482 guard bleManager. infiniTime != nil else { return }
6583
6684 let hexPrefix = Data ( [ 0x03 , 0x01 , 0x00 ] ) // Hexadecimal representation of "\x03\x01\x00"
67- let nameData = " InfiniLink " . data ( using: . ascii) ?? Data ( )
85+ let nameData = " InfiniLink " . data ( using: . ascii) !
6886
6987 let notification = hexPrefix + nameData
7088
@@ -86,7 +104,10 @@ struct BLEWriteManager {
86104 guard var locationData = location. data ( using: . ascii) else {
87105 log ( " Error encoding location string " , caller: " BLEWriteManager " )
88106
89- for _ in 1 ... 32 { bytes. append ( 0 ) }
107+ for _ in 1 ... 32 {
108+ bytes. append ( 0 )
109+ }
110+
90111 bytes. append ( icon)
91112
92113 let writeData = Data ( bytes: bytes as [ UInt8 ] , count: 49 )
@@ -98,9 +119,13 @@ struct BLEWriteManager {
98119
99120 if locationData. count > 32 {
100121 log ( " Weather location string is too big to send " , caller: " BLEWriteManager " , target: . ble)
101- for _ in 1 ... 32 { bytes. append ( 0 ) }
122+ for _ in 1 ... 32 {
123+ bytes. append ( 0 )
124+ }
102125 } else {
103- for _ in 1 ... 32 - locationData. count { locationData. append ( 0 ) }
126+ for _ in ( 1 ... 32 - locationData. count) {
127+ locationData. append ( 0 )
128+ }
104129 bytes. append ( contentsOf: locationData)
105130 }
106131 bytes. append ( icon)
@@ -124,14 +149,14 @@ struct BLEWriteManager {
124149 bytes. append ( contentsOf: timeSince1970 ( ) )
125150 bytes. append ( UInt8 ( minimumTemperature. count) )
126151
127- for idx in 0 ... minimumTemperature. count- 1 {
152+ for idx in ( 0 ... minimumTemperature. count - 1 ) {
128153 bytes. append ( contentsOf: convertTemperature ( value: Int ( round ( minimumTemperature [ idx] ) ) ) ) // Minimum temperature
129154 bytes. append ( contentsOf: convertTemperature ( value: Int ( round ( maximumTemperature [ idx] ) ) ) ) // Maximum temperature
130155 bytes. append ( icon [ idx] )
131156 }
132157
133158 if minimumTemperature. count < 5 {
134- for _ in 0 ... 4 - minimumTemperature. count {
159+ for _ in ( 0 ... 4 - minimumTemperature. count) {
135160 bytes. append ( contentsOf: [ 0 , 0 , 0 , 0 , 0 ] )
136161 }
137162 }
@@ -149,7 +174,8 @@ struct BLEWriteManager {
149174 guard bleManager. navigationFlagsCharacteristic != nil && bleManager. navigationNarrativeCharacteristic != nil && bleManager. navigationDistanceCharacteristic != nil && bleManager. navigationProgressCharacteristic != nil && bleManager. infiniTime != nil else { return }
150175
151176 guard let icon = icon. data ( using: . ascii) else { return }
152- guard let narrative = instructions. data ( using: . ascii) else { return }
177+ // The narrative may contain non-InfiniTime-readable characters, so transliterate if enabled
178+ guard let narrative = ( transliterationEnabled ? instructions. asciiSafe : instructions) . data ( using: . ascii) else { return }
153179 guard let distance = distance. data ( using: . ascii) else { return }
154180
155181 var progress = Data ( )
@@ -166,7 +192,7 @@ struct BLEWriteManager {
166192
167193extension BLEWriteManager {
168194 func timeSince1970( ) -> [ UInt8 ] {
169- let timeInterval : UInt64 = UInt64 ( Date ( ) . timeIntervalSince1970)
195+ let timeInterval : UInt64 = UInt64 ( Date ( ) . timeIntervalSince1970)
170196
171197 let byte1 = UInt8 ( timeInterval & 0x00000000000000FF )
172198 let byte2 = UInt8 ( ( timeInterval & 0x000000000000FF00 ) >> 8 )
0 commit comments