{ "id": "62351", "key": "TIMOB-1719", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [], "resolution": { "id": "2", "description": "The problem described is an issue which will never be fixed.", "name": "Won't Fix" }, "resolutiondate": "2017-07-26T03:23:04.000+0000", "created": "2011-04-15T03:00:26.000+0000", "priority": { "name": "High", "id": "2" }, "labels": [ "core" ], "versions": [ { "id": "13505", "description": "Release 3.0.0", "name": "Release 3.0.0", "archived": true, "released": true, "releaseDate": "2012-12-14" } ], "issuelinks": [], "assignee": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2017-07-26T03:23:04.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "{html}

This was addressed on Android, over to iOS for parity. Below are\r\nthe notes Bill wrote in the original bug. From #1690

\r\n

Use case (helpdesk 38891): app fetches data via XHR. Data is XML\r\nencoded in ISO-8859-1. Data is saved to file, to be opened later.\r\nWhen re-opened, Titanium forces it to UTF-8, which screws up the\r\nencoding of some of the special characters such as those with\r\numlaute (ö, ä etc) or accents (é etc).

\r\n

Fail case:

\r\n

script to repro:

\r\n

Titanium.UI.setBackgroundColor('#000');
\r\nvar win = Titanium.UI.createWindow({

\r\n
\r\ntitle:'Feed encoding test',\r\nbackgroundColor:'#fff',\r\nfullscreen: true,\r\nexitOnClose: true,\r\nurl: 'main.js'\r\n
\r\n

}); win.open();
\r\nmain.js:

\r\n

var win = Ti.UI.currentWindow;
\r\nwin.title = \"Feed encoding via file\"
\r\nvar url = [SEE HELPDESK 38891]

\r\n

var dir = Titanium.Filesystem.applicationDataDirectory;
\r\nvar filename = 'feed.xml';
\r\nvar file = Ti.Filesystem.getFile(dir, filename);

\r\n

function saveFeed(data) {

\r\n
\r\nif (file.exists()){\r\n    file.deleteFile();\r\n}\r\nfile.write(data);\r\n
\r\n

}

\r\n

function buildTable() {

\r\n
\r\nvar blob = file.read();\r\nvar string = blob.text;\r\nvar xml = Ti.XML.parseString(string);\r\nvar rows = [];\r\ntry {\r\n    if (xml) {\r\n        var session = xml.getElementsByTagName('session');\r\n        if (session) {\r\n            session = session.item(0);\r\n        } else { \r\n            alert('session list not fetched');\r\n            return;\r\n        }\r\n        if (session) {\r\n            if (!session.hasChildNodes()) {\r\n                alert('No child nodes');\r\n                return;\r\n            }\r\n            var length = session.childNodes.length;\r\n            for (var i = 0; i < length; i++) {\r\n                var child = session.childNodes.item(i);\r\n                if (child.nodeType == child.ELEMENT_NODE) {\r\n                    rows.push(Ti.UI.createTableViewRow({color: 'black', title: child.getAttribute(\"driver\")}));\r\n                }\r\n            }\r\n            win.add(Ti.UI.createTableView({data: rows}));\r\n        } else {\r\n            alert('\"session\" not found');\r\n        }\r\n    } else {\r\n        alert('XML did not load');\r\n    }\r\n} catch(ex) {\r\n    alert(ex);\r\n}\r\n
\r\n

}

\r\n

var xhr = Ti.Network.createHTTPClient();
\r\nxhr.onload = function(e) {

\r\n
\r\ntry {\r\n    var data = xhr.responseData;\r\n    saveFeed(data);\r\n    buildTable();\r\n\r\n} catch(ex) {\r\n    alert(ex);\r\n}\r\n
\r\n

}; xhr.open('GET', url);
\r\nxhr.send();

{html}", "attachment": [], "flagged": false, "summary": "iOS: non-UTF-8 data from external source cannot be saved and reopened accurately", "creator": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "comment": { "comments": [ { "id": "126442", "author": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "body": "{html}

more of Bills notes:
\nWe want to keep things internally in UTF-8, so instead of trying to\ntrack the other encoding throughout its lifetime in Titanium code\n(i.e., in TiBlob, TiFile, etc), we now give the developer a way to\ntranscode the text to utf-8 and then save that:
\nvar utf8Text = Ti.Utils.transcodeString(origText, 'ISO-8859-1',\n'UTF-8');
\nSo Ti.Utils.transcodeString(origText, origEncoding,\ndesiredEncoding) is new.
\nTo fix the fail case from the description of this item, in the\nfunction saveFeed change file.write(data) to\nfile.write(Ti.Utils.transcodeString(data, 'ISO-8859-1',\n'UTF-8')).
\nThen, because the feed contains <?xml version=\"1.0\"\nencoding=\"ISO-8859-1\"?>, you need to change the encoding= to\nread encoding='UTF-8 before giving it to the xml parser. So in\nbuildTable() change ...
\nvar xml = Ti.XML.parseString(string);
\n... to ... var xml =\nTi.XML.parseString(string.replace(/iso-8859-1/i, 'UTF-8'));
\nFinally, when the data comes back from the provider, get it with\nresponseText instead of responseData
\n. So replace this... var data = xhr.responseData;
\nwith this ...

\n

var data = xhr.responseText;

\n

also

\n

xhr.responseData with xhr.responseText

{html}", "updateAuthor": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-04-15T03:00:26.000+0000", "updated": "2011-04-15T03:00:26.000+0000" }, { "id": "126443", "author": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "body": "{html}

the current behavior is the test generates an alert on iOS. I\nposted the working test case (resource files) to\nAppcelerator/Users/Thomas/bugs/1719 if that helpful

{html}", "updateAuthor": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-04-15T03:00:26.000+0000", "updated": "2011-04-15T03:00:26.000+0000" }, { "id": "126444", "author": { "name": "billdawson", "key": "billdawson", "displayName": "Bill Dawson", "active": true, "timeZone": "Europe/Berlin" }, "body": "{html}

We added Ti.Utils.transcodeString, so Ti iOS should\nget one too for parity. Here's the java, fyi:

\n
\npublic String transcodeString(String orig, String inEncoding, String outEncoding)\n    {\n        try {\n            \n            Charset charsetOut = Charset.forName(outEncoding);\n            Charset charsetIn = Charset.forName(inEncoding);\n\n            ByteBuffer bufferIn = ByteBuffer.wrap(orig.getBytes(charsetIn.name()) );\n            CharBuffer dataIn = charsetIn.decode(bufferIn);\n            bufferIn.clear();\n            bufferIn = null;\n\n            ByteBuffer bufferOut = charsetOut.encode(dataIn);\n            dataIn.clear();\n            dataIn = null;\n            byte[] dataOut = bufferOut.array();\n            bufferOut.clear();\n            bufferOut = null;\n            \n            return new String(dataOut, charsetOut.name());\n            \n        } catch (UnsupportedEncodingException e) {\n            Log.e(LCAT, \"Unsupported encoding: \" + e.getMessage(), e);\n        }\n        return null;\n    }\n
{html}", "updateAuthor": { "name": "billdawson", "key": "billdawson", "displayName": "Bill Dawson", "active": true, "timeZone": "Europe/Berlin" }, "created": "2011-04-15T03:00:26.000+0000", "updated": "2011-04-15T03:00:26.000+0000" }, { "id": "126445", "author": { "name": "blainhamon", "key": "blainhamon", "displayName": "Blain Hamon", "active": true, "timeZone": "America/Los_Angeles" }, "body": "{html}

We could leave this as an entrance test for new hires. Very\nclean and clearcut function.

{html}", "updateAuthor": { "name": "blainhamon", "key": "blainhamon", "displayName": "Blain Hamon", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-04-15T03:00:27.000+0000", "updated": "2011-04-15T03:00:27.000+0000" }, { "id": "209498", "author": { "name": "stephentramer", "key": "stephentramer", "displayName": "Stephen Tramer", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Despite the lack of a working existing test case (no URL provided in test) it is clear that this bug is still valid in SDK 2.2.0.014b86f because we have NO UTF-16 support. Options are:\r\n\r\n# Provide an alternative test from original customer\r\n# Find a UTF-16 datastream to test against\r\n\r\nTagging for core as it is part of a broader UTF-16 support push.", "updateAuthor": { "name": "stephentramer", "key": "stephentramer", "displayName": "Stephen Tramer", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2012-07-25T17:53:28.000+0000", "updated": "2012-07-25T17:53:28.000+0000" }, { "id": "424722", "author": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing due to inactivity. If this issue still exists, please raise a new ticket.", "updateAuthor": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-07-26T03:23:04.000+0000", "updated": "2017-07-26T03:23:04.000+0000" } ], "maxResults": 6, "total": 6, "startAt": 0 } } }