\n\n","xml",[101,1034,1035,1040,1045,1050,1055,1060,1065,1070,1075],{"__ignoreMap":26},[104,1036,1037],{"class":106,"line":107},[104,1038,1039],{"emptyLinePlaceholder":36},"\n",[104,1041,1042],{"class":106,"line":27},[104,1043,1044],{},"\u003Ckey>Type\u003C/key>\n",[104,1046,1047],{"class":106,"line":137},[104,1048,1049],{},"\u003Cstring>PSToggleSwitchSpecifier\u003C/string>\n",[104,1051,1052],{"class":106,"line":161},[104,1053,1054],{},"\u003Ckey>Title\u003C/key>\n",[104,1056,1057],{"class":106,"line":174},[104,1058,1059],{},"\u003Cstring>Sound\u003C/string>\n",[104,1061,1062],{"class":106,"line":180},[104,1063,1064],{},"\u003Ckey>Key\u003C/key>\n",[104,1066,1067],{"class":106,"line":197},[104,1068,1069],{},"\u003Cstring>sound_enabled\u003C/string>\n",[104,1071,1072],{"class":106,"line":209},[104,1073,1074],{},"\u003Ckey>DefaultValue\u003C/key>\n",[104,1076,1077],{"class":106,"line":226},[104,1078,1079],{},"\u003Ctrue/>\n",[18,1081,1082,1083,1088,1089,1092],{},"Unfortuntately, the default value is only applied the first time you access the Settings Application. That means your\ncode cannot rely on the default values and rather has to check manually, if the value has been set in the Settings\nApplication. In case of “I think I spider”, that meant no sound until you accessed the Settings Application. After\npoking around for a couple of minutes,\nwe ",[60,1084,1087],{"href":1085,"rel":1086},"http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the-s/510329#510329",[64],"found a workaround",",\nwhich we implemented in our AppDelegate’s ",[318,1090,1091],{},"didFinishLaunchingWithOptions"," method:",[95,1094,1096],{"className":875,"code":1095,"language":877,"meta":26,"style":26},"\nid test = [[NSUserDefaults standardUserDefaults] objectForKey:@\"sound_enabled\"];\nif (test == NULL) {\n [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@\"sound_enabled\"];\n}\nreturn YES;\n",[101,1097,1098,1102,1107,1112,1117,1121],{"__ignoreMap":26},[104,1099,1100],{"class":106,"line":107},[104,1101,1039],{"emptyLinePlaceholder":36},[104,1103,1104],{"class":106,"line":27},[104,1105,1106],{},"id test = [[NSUserDefaults standardUserDefaults] objectForKey:@\"sound_enabled\"];\n",[104,1108,1109],{"class":106,"line":137},[104,1110,1111],{},"if (test == NULL) {\n",[104,1113,1114],{"class":106,"line":161},[104,1115,1116],{}," [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@\"sound_enabled\"];\n",[104,1118,1119],{"class":106,"line":174},[104,1120,313],{},[104,1122,1123],{"class":106,"line":180},[104,1124,1125],{},"return YES;\n",[18,1127,1128,1129,1132,1133,1136],{},"This checks if no value was set, which means the returned value is ",[318,1130,1131],{},"NULL",", and in that case sets our default value. Note\nthat we are duplicating the default value definition here. You could also access the ",[318,1134,1135],{},"Settings Bundle"," programmatically\nand read the default value from there.",[18,1138,1139],{},"This gives us an easy workaround, so that you can enjoy the awesome sound effects in “I think I spider”.",[352,1141,930],{},{"title":26,"searchDepth":27,"depth":27,"links":1143},[],[30,31,358],"2010-08-16T06:38:08","https://synyx.de/blog/settings-bundle-and-default-values/",{},"/blog/settings-bundle-and-default-values",{"title":1017,"description":26},"blog/settings-bundle-and-default-values",[42,43],"We improved our “I think I spider” App quite a bit since Beta 1. Among other stuff we added some nice sound effects. If you want to check it out,…","2u74Bt9iJYUS2yCQZEziD6_UMO-mCCEs4rBEu1KVQJQ",{"id":1155,"title":1156,"author":1157,"body":1158,"category":2757,"date":2758,"description":2759,"extension":33,"link":2760,"meta":2761,"navigation":36,"path":2762,"seo":2763,"slug":1162,"stem":2765,"tags":2766,"teaser":2767,"__hash__":2768},"blog/blog/on-cross-device-mobile-development-part-1.md","On cross-device mobile development – Part 1",[552],{"type":11,"value":1159,"toc":2751},[1160,1163,1170,1174,1177,1181,1190,1506,1509,2253,2256,2464,2475,2527,2530,2700,2704,2714,2717,2724,2728,2731,2745,2748],[14,1161,1156],{"id":1162},"on-cross-device-mobile-development-part-1",[18,1164,1165,1166,1169],{},"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\ndeployment target for mobile applications. ",[318,1167,1168],{},"That’s the theory at least."," In this series of articles we will provide an\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\nseveral mobile platforms at once.",[572,1171,1173],{"id":1172},"its-not-in-the-browser","It’s (not) in the browser",[18,1175,1176],{},"Developing applications with HTML, CSS & Javascript is a fundamentally different experience. It already is on the\nnormal desktop with its widescreen browsers. On a mobile device it becomes a game changer. We are accustomed to\ngraphical user interface (GUI) toolkits with their more direct manipulation of data through on-screen elements like\nbuttons. While browsers and HTML provide their own set of UI elements and the ability to wire events like clicks to\nJavascript callbacks, they lack a lot of the bells and whistles. Especially in the handling of client-side data — the\nmodel in model-view-controller (MVC) so to say — and the controller abstraction even modern browsers lack the feature\nrichness of common native SDKs. In the rest of this article we will give a short overview on which web technologies map\nto which concept in the MVC approach and how you would basically structure an application based on such technologies.",[572,1178,1180],{"id":1179},"of-models-views-controllers","Of models, views & controllers",[18,1182,1183,1184,1189],{},"In ",[60,1185,1188],{"href":1186,"rel":1187},"http://building-iphone-apps.labs.oreilly.com/ch04.html",[64],"“Building iPhone Apps with HTML, CSS and JavaScript”","\nJonathan Stark describes how to turn websites into single page iPhone applications. Views are implemented straight\nforward with plain HTML and CSS, throwing in some bits and pieces to make browser standard behavior of HTML elements\nmore native like. To give an example, we will show how to create a graphical navigation bar and switch between different\nscreens. First we need some HTML and CSS snippets to create the layout:",[95,1191,1194],{"className":1192,"code":1193,"language":833,"meta":26,"style":26},"language-html shiki shiki-themes github-light github-dark","\u003Cul id=\"navigation\">\n \u003Cli>\u003Ca class=\"current\" href=\"#home\">Home\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#new\">New things\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#favorites\">I like those\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#more\">More content\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#info\">About\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"view\" id=\"home\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"new\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"favorites\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"more\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"info\">...\u003C/div>\n",[101,1195,1196,1216,1257,1285,1313,1341,1369,1378,1406,1431,1456,1481],{"__ignoreMap":26},[104,1197,1198,1201,1205,1208,1210,1213],{"class":106,"line":107},[104,1199,1200],{"class":120},"\u003C",[104,1202,1204],{"class":1203},"s9eBZ","ul",[104,1206,1207],{"class":130}," id",[104,1209,124],{"class":120},[104,1211,1212],{"class":190},"\"navigation\"",[104,1214,1215],{"class":120},">\n",[104,1217,1218,1221,1224,1227,1229,1232,1234,1237,1240,1242,1245,1248,1250,1253,1255],{"class":106,"line":27},[104,1219,1220],{"class":120}," \u003C",[104,1222,1223],{"class":1203},"li",[104,1225,1226],{"class":120},">\u003C",[104,1228,60],{"class":1203},[104,1230,1231],{"class":130}," class",[104,1233,124],{"class":120},[104,1235,1236],{"class":190},"\"current\"",[104,1238,1239],{"class":130}," href",[104,1241,124],{"class":120},[104,1243,1244],{"class":190},"\"#home\"",[104,1246,1247],{"class":120},">Home\u003C/",[104,1249,60],{"class":1203},[104,1251,1252],{"class":120},">\u003C/",[104,1254,1223],{"class":1203},[104,1256,1215],{"class":120},[104,1258,1259,1261,1263,1265,1267,1269,1271,1274,1277,1279,1281,1283],{"class":106,"line":137},[104,1260,1220],{"class":120},[104,1262,1223],{"class":1203},[104,1264,1226],{"class":120},[104,1266,60],{"class":1203},[104,1268,1239],{"class":130},[104,1270,124],{"class":120},[104,1272,1273],{"class":190},"\"#new\"",[104,1275,1276],{"class":120},">New things\u003C/",[104,1278,60],{"class":1203},[104,1280,1252],{"class":120},[104,1282,1223],{"class":1203},[104,1284,1215],{"class":120},[104,1286,1287,1289,1291,1293,1295,1297,1299,1302,1305,1307,1309,1311],{"class":106,"line":161},[104,1288,1220],{"class":120},[104,1290,1223],{"class":1203},[104,1292,1226],{"class":120},[104,1294,60],{"class":1203},[104,1296,1239],{"class":130},[104,1298,124],{"class":120},[104,1300,1301],{"class":190},"\"#favorites\"",[104,1303,1304],{"class":120},">I like those\u003C/",[104,1306,60],{"class":1203},[104,1308,1252],{"class":120},[104,1310,1223],{"class":1203},[104,1312,1215],{"class":120},[104,1314,1315,1317,1319,1321,1323,1325,1327,1330,1333,1335,1337,1339],{"class":106,"line":174},[104,1316,1220],{"class":120},[104,1318,1223],{"class":1203},[104,1320,1226],{"class":120},[104,1322,60],{"class":1203},[104,1324,1239],{"class":130},[104,1326,124],{"class":120},[104,1328,1329],{"class":190},"\"#more\"",[104,1331,1332],{"class":120},">More content\u003C/",[104,1334,60],{"class":1203},[104,1336,1252],{"class":120},[104,1338,1223],{"class":1203},[104,1340,1215],{"class":120},[104,1342,1343,1345,1347,1349,1351,1353,1355,1358,1361,1363,1365,1367],{"class":106,"line":180},[104,1344,1220],{"class":120},[104,1346,1223],{"class":1203},[104,1348,1226],{"class":120},[104,1350,60],{"class":1203},[104,1352,1239],{"class":130},[104,1354,124],{"class":120},[104,1356,1357],{"class":190},"\"#info\"",[104,1359,1360],{"class":120},">About\u003C/",[104,1362,60],{"class":1203},[104,1364,1252],{"class":120},[104,1366,1223],{"class":1203},[104,1368,1215],{"class":120},[104,1370,1371,1374,1376],{"class":106,"line":197},[104,1372,1373],{"class":120},"\u003C/",[104,1375,1204],{"class":1203},[104,1377,1215],{"class":120},[104,1379,1380,1382,1385,1387,1389,1392,1394,1396,1399,1402,1404],{"class":106,"line":209},[104,1381,1200],{"class":120},[104,1383,1384],{"class":1203},"div",[104,1386,1231],{"class":130},[104,1388,124],{"class":120},[104,1390,1391],{"class":190},"\"view\"",[104,1393,1207],{"class":130},[104,1395,124],{"class":120},[104,1397,1398],{"class":190},"\"home\"",[104,1400,1401],{"class":120},">...\u003C/",[104,1403,1384],{"class":1203},[104,1405,1215],{"class":120},[104,1407,1408,1410,1412,1414,1416,1418,1420,1422,1425,1427,1429],{"class":106,"line":226},[104,1409,1200],{"class":120},[104,1411,1384],{"class":1203},[104,1413,1231],{"class":130},[104,1415,124],{"class":120},[104,1417,1391],{"class":190},[104,1419,1207],{"class":130},[104,1421,124],{"class":120},[104,1423,1424],{"class":190},"\"new\"",[104,1426,1401],{"class":120},[104,1428,1384],{"class":1203},[104,1430,1215],{"class":120},[104,1432,1433,1435,1437,1439,1441,1443,1445,1447,1450,1452,1454],{"class":106,"line":253},[104,1434,1200],{"class":120},[104,1436,1384],{"class":1203},[104,1438,1231],{"class":130},[104,1440,124],{"class":120},[104,1442,1391],{"class":190},[104,1444,1207],{"class":130},[104,1446,124],{"class":120},[104,1448,1449],{"class":190},"\"favorites\"",[104,1451,1401],{"class":120},[104,1453,1384],{"class":1203},[104,1455,1215],{"class":120},[104,1457,1458,1460,1462,1464,1466,1468,1470,1472,1475,1477,1479],{"class":106,"line":277},[104,1459,1200],{"class":120},[104,1461,1384],{"class":1203},[104,1463,1231],{"class":130},[104,1465,124],{"class":120},[104,1467,1391],{"class":190},[104,1469,1207],{"class":130},[104,1471,124],{"class":120},[104,1473,1474],{"class":190},"\"more\"",[104,1476,1401],{"class":120},[104,1478,1384],{"class":1203},[104,1480,1215],{"class":120},[104,1482,1483,1485,1487,1489,1491,1493,1495,1497,1500,1502,1504],{"class":106,"line":288},[104,1484,1200],{"class":120},[104,1486,1384],{"class":1203},[104,1488,1231],{"class":130},[104,1490,124],{"class":120},[104,1492,1391],{"class":190},[104,1494,1207],{"class":130},[104,1496,124],{"class":120},[104,1498,1499],{"class":190},"\"info\"",[104,1501,1401],{"class":120},[104,1503,1384],{"class":1203},[104,1505,1215],{"class":120},[18,1507,1508],{},"The navigational items are then replaced with nice button graphics via CSS and positioned on the page.",[95,1510,1513],{"className":1511,"code":1512,"language":832,"meta":26,"style":26},"language-css shiki shiki-themes github-light github-dark","/* navigation is a fixed block */\n#navigation {\n position: fixed;\n top: 0px;\n left: 0px;\n width: 320px;\n margin: 0;\n padding: 0;\n}\n#navigation li {\n display: block;\n position: absolute;\n}\n#navigation li a {\n display: block;\n position: absolute;\n height: 48px;\n width: 80px;\n top: 0px;\n text-indent: -9999px;\n}\na[href=\"#home\"] {\n background: url(home.png);\n left: 0px;\n}\na[href=\"#home\"].current {\n background: url(home-current.png);\n}\na[href=\"#new\"] {\n background: url(new.png);\n left: 80px;\n}\na[href=\"#new\"].current {\n background: url(new-current.png);\n}\na[href=\"#favorites\"] {\n background: url(favorites.png);\n left: 160px;\n}\na[href=\"#favorites\"].current {\n background: url(favorites-current.png);\n}\na[href=\"#more\"] {\n background: url(more.png);\n left: 240px;\n}\na[href=\"#info\"] {\n background: url(info.png);\n position: fixed;\n width: 48px;\n height: 48px;\n bottom: 16px;\n right: 16px;\n}\n/* finally position the views themselves */\ndiv.view {\n position: absolute;\n top: 48px;\n left: 0px;\n width: 320px;\n height: 396px;\n}\n",[101,1514,1515,1520,1527,1541,1556,1569,1583,1594,1605,1609,1618,1630,1641,1645,1656,1667,1678,1693,1707,1720,1735,1740,1758,1777,1790,1795,1816,1832,1837,1852,1868,1881,1886,1905,1921,1926,1941,1957,1971,1976,1995,2011,2016,2031,2047,2061,2066,2081,2097,2108,2121,2134,2149,2163,2168,2174,2184,2195,2208,2221,2234,2248],{"__ignoreMap":26},[104,1516,1517],{"class":106,"line":107},[104,1518,1519],{"class":110},"/* navigation is a fixed block */\n",[104,1521,1522,1525],{"class":106,"line":27},[104,1523,1524],{"class":130},"#navigation",[104,1526,297],{"class":120},[104,1528,1529,1533,1536,1539],{"class":106,"line":137},[104,1530,1532],{"class":1531},"sj4cs"," position",[104,1534,1535],{"class":120},": ",[104,1537,1538],{"class":1531},"fixed",[104,1540,194],{"class":120},[104,1542,1543,1546,1548,1551,1554],{"class":106,"line":161},[104,1544,1545],{"class":1531}," top",[104,1547,1535],{"class":120},[104,1549,1550],{"class":1531},"0",[104,1552,1553],{"class":116},"px",[104,1555,194],{"class":120},[104,1557,1558,1561,1563,1565,1567],{"class":106,"line":174},[104,1559,1560],{"class":1531}," left",[104,1562,1535],{"class":120},[104,1564,1550],{"class":1531},[104,1566,1553],{"class":116},[104,1568,194],{"class":120},[104,1570,1571,1574,1576,1579,1581],{"class":106,"line":180},[104,1572,1573],{"class":1531}," width",[104,1575,1535],{"class":120},[104,1577,1578],{"class":1531},"320",[104,1580,1553],{"class":116},[104,1582,194],{"class":120},[104,1584,1585,1588,1590,1592],{"class":106,"line":197},[104,1586,1587],{"class":1531}," margin",[104,1589,1535],{"class":120},[104,1591,1550],{"class":1531},[104,1593,194],{"class":120},[104,1595,1596,1599,1601,1603],{"class":106,"line":209},[104,1597,1598],{"class":1531}," padding",[104,1600,1535],{"class":120},[104,1602,1550],{"class":1531},[104,1604,194],{"class":120},[104,1606,1607],{"class":106,"line":226},[104,1608,313],{"class":120},[104,1610,1611,1613,1616],{"class":106,"line":253},[104,1612,1524],{"class":130},[104,1614,1615],{"class":1203}," li",[104,1617,297],{"class":120},[104,1619,1620,1623,1625,1628],{"class":106,"line":277},[104,1621,1622],{"class":1531}," display",[104,1624,1535],{"class":120},[104,1626,1627],{"class":1531},"block",[104,1629,194],{"class":120},[104,1631,1632,1634,1636,1639],{"class":106,"line":288},[104,1633,1532],{"class":1531},[104,1635,1535],{"class":120},[104,1637,1638],{"class":1531},"absolute",[104,1640,194],{"class":120},[104,1642,1643],{"class":106,"line":300},[104,1644,313],{"class":120},[104,1646,1647,1649,1651,1654],{"class":106,"line":310},[104,1648,1524],{"class":130},[104,1650,1615],{"class":1203},[104,1652,1653],{"class":1203}," a",[104,1655,297],{"class":120},[104,1657,1659,1661,1663,1665],{"class":106,"line":1658},15,[104,1660,1622],{"class":1531},[104,1662,1535],{"class":120},[104,1664,1627],{"class":1531},[104,1666,194],{"class":120},[104,1668,1670,1672,1674,1676],{"class":106,"line":1669},16,[104,1671,1532],{"class":1531},[104,1673,1535],{"class":120},[104,1675,1638],{"class":1531},[104,1677,194],{"class":120},[104,1679,1681,1684,1686,1689,1691],{"class":106,"line":1680},17,[104,1682,1683],{"class":1531}," height",[104,1685,1535],{"class":120},[104,1687,1688],{"class":1531},"48",[104,1690,1553],{"class":116},[104,1692,194],{"class":120},[104,1694,1696,1698,1700,1703,1705],{"class":106,"line":1695},18,[104,1697,1573],{"class":1531},[104,1699,1535],{"class":120},[104,1701,1702],{"class":1531},"80",[104,1704,1553],{"class":116},[104,1706,194],{"class":120},[104,1708,1710,1712,1714,1716,1718],{"class":106,"line":1709},19,[104,1711,1545],{"class":1531},[104,1713,1535],{"class":120},[104,1715,1550],{"class":1531},[104,1717,1553],{"class":116},[104,1719,194],{"class":120},[104,1721,1723,1726,1728,1731,1733],{"class":106,"line":1722},20,[104,1724,1725],{"class":1531}," text-indent",[104,1727,1535],{"class":120},[104,1729,1730],{"class":1531},"-9999",[104,1732,1553],{"class":116},[104,1734,194],{"class":120},[104,1736,1738],{"class":106,"line":1737},21,[104,1739,313],{"class":120},[104,1741,1743,1745,1748,1751,1753,1755],{"class":106,"line":1742},22,[104,1744,60],{"class":1203},[104,1746,1747],{"class":120},"[",[104,1749,1750],{"class":130},"href",[104,1752,124],{"class":116},[104,1754,1244],{"class":190},[104,1756,1757],{"class":120},"] {\n",[104,1759,1761,1764,1766,1769,1771,1775],{"class":106,"line":1760},23,[104,1762,1763],{"class":1531}," background",[104,1765,1535],{"class":120},[104,1767,1768],{"class":1531},"url",[104,1770,217],{"class":120},[104,1772,1774],{"class":1773},"s4XuR","home.png",[104,1776,223],{"class":120},[104,1778,1780,1782,1784,1786,1788],{"class":106,"line":1779},24,[104,1781,1560],{"class":1531},[104,1783,1535],{"class":120},[104,1785,1550],{"class":1531},[104,1787,1553],{"class":116},[104,1789,194],{"class":120},[104,1791,1793],{"class":106,"line":1792},25,[104,1794,313],{"class":120},[104,1796,1798,1800,1802,1804,1806,1808,1811,1814],{"class":106,"line":1797},26,[104,1799,60],{"class":1203},[104,1801,1747],{"class":120},[104,1803,1750],{"class":130},[104,1805,124],{"class":116},[104,1807,1244],{"class":190},[104,1809,1810],{"class":120},"]",[104,1812,1813],{"class":130},".current",[104,1815,297],{"class":120},[104,1817,1819,1821,1823,1825,1827,1830],{"class":106,"line":1818},27,[104,1820,1763],{"class":1531},[104,1822,1535],{"class":120},[104,1824,1768],{"class":1531},[104,1826,217],{"class":120},[104,1828,1829],{"class":1773},"home-current.png",[104,1831,223],{"class":120},[104,1833,1835],{"class":106,"line":1834},28,[104,1836,313],{"class":120},[104,1838,1840,1842,1844,1846,1848,1850],{"class":106,"line":1839},29,[104,1841,60],{"class":1203},[104,1843,1747],{"class":120},[104,1845,1750],{"class":130},[104,1847,124],{"class":116},[104,1849,1273],{"class":190},[104,1851,1757],{"class":120},[104,1853,1855,1857,1859,1861,1863,1866],{"class":106,"line":1854},30,[104,1856,1763],{"class":1531},[104,1858,1535],{"class":120},[104,1860,1768],{"class":1531},[104,1862,217],{"class":120},[104,1864,1865],{"class":1773},"new.png",[104,1867,223],{"class":120},[104,1869,1871,1873,1875,1877,1879],{"class":106,"line":1870},31,[104,1872,1560],{"class":1531},[104,1874,1535],{"class":120},[104,1876,1702],{"class":1531},[104,1878,1553],{"class":116},[104,1880,194],{"class":120},[104,1882,1884],{"class":106,"line":1883},32,[104,1885,313],{"class":120},[104,1887,1889,1891,1893,1895,1897,1899,1901,1903],{"class":106,"line":1888},33,[104,1890,60],{"class":1203},[104,1892,1747],{"class":120},[104,1894,1750],{"class":130},[104,1896,124],{"class":116},[104,1898,1273],{"class":190},[104,1900,1810],{"class":120},[104,1902,1813],{"class":130},[104,1904,297],{"class":120},[104,1906,1908,1910,1912,1914,1916,1919],{"class":106,"line":1907},34,[104,1909,1763],{"class":1531},[104,1911,1535],{"class":120},[104,1913,1768],{"class":1531},[104,1915,217],{"class":120},[104,1917,1918],{"class":1773},"new-current.png",[104,1920,223],{"class":120},[104,1922,1924],{"class":106,"line":1923},35,[104,1925,313],{"class":120},[104,1927,1929,1931,1933,1935,1937,1939],{"class":106,"line":1928},36,[104,1930,60],{"class":1203},[104,1932,1747],{"class":120},[104,1934,1750],{"class":130},[104,1936,124],{"class":116},[104,1938,1301],{"class":190},[104,1940,1757],{"class":120},[104,1942,1944,1946,1948,1950,1952,1955],{"class":106,"line":1943},37,[104,1945,1763],{"class":1531},[104,1947,1535],{"class":120},[104,1949,1768],{"class":1531},[104,1951,217],{"class":120},[104,1953,1954],{"class":1773},"favorites.png",[104,1956,223],{"class":120},[104,1958,1960,1962,1964,1967,1969],{"class":106,"line":1959},38,[104,1961,1560],{"class":1531},[104,1963,1535],{"class":120},[104,1965,1966],{"class":1531},"160",[104,1968,1553],{"class":116},[104,1970,194],{"class":120},[104,1972,1974],{"class":106,"line":1973},39,[104,1975,313],{"class":120},[104,1977,1979,1981,1983,1985,1987,1989,1991,1993],{"class":106,"line":1978},40,[104,1980,60],{"class":1203},[104,1982,1747],{"class":120},[104,1984,1750],{"class":130},[104,1986,124],{"class":116},[104,1988,1301],{"class":190},[104,1990,1810],{"class":120},[104,1992,1813],{"class":130},[104,1994,297],{"class":120},[104,1996,1998,2000,2002,2004,2006,2009],{"class":106,"line":1997},41,[104,1999,1763],{"class":1531},[104,2001,1535],{"class":120},[104,2003,1768],{"class":1531},[104,2005,217],{"class":120},[104,2007,2008],{"class":1773},"favorites-current.png",[104,2010,223],{"class":120},[104,2012,2014],{"class":106,"line":2013},42,[104,2015,313],{"class":120},[104,2017,2019,2021,2023,2025,2027,2029],{"class":106,"line":2018},43,[104,2020,60],{"class":1203},[104,2022,1747],{"class":120},[104,2024,1750],{"class":130},[104,2026,124],{"class":116},[104,2028,1329],{"class":190},[104,2030,1757],{"class":120},[104,2032,2034,2036,2038,2040,2042,2045],{"class":106,"line":2033},44,[104,2035,1763],{"class":1531},[104,2037,1535],{"class":120},[104,2039,1768],{"class":1531},[104,2041,217],{"class":120},[104,2043,2044],{"class":1773},"more.png",[104,2046,223],{"class":120},[104,2048,2050,2052,2054,2057,2059],{"class":106,"line":2049},45,[104,2051,1560],{"class":1531},[104,2053,1535],{"class":120},[104,2055,2056],{"class":1531},"240",[104,2058,1553],{"class":116},[104,2060,194],{"class":120},[104,2062,2064],{"class":106,"line":2063},46,[104,2065,313],{"class":120},[104,2067,2069,2071,2073,2075,2077,2079],{"class":106,"line":2068},47,[104,2070,60],{"class":1203},[104,2072,1747],{"class":120},[104,2074,1750],{"class":130},[104,2076,124],{"class":116},[104,2078,1357],{"class":190},[104,2080,1757],{"class":120},[104,2082,2084,2086,2088,2090,2092,2095],{"class":106,"line":2083},48,[104,2085,1763],{"class":1531},[104,2087,1535],{"class":120},[104,2089,1768],{"class":1531},[104,2091,217],{"class":120},[104,2093,2094],{"class":1773},"info.png",[104,2096,223],{"class":120},[104,2098,2100,2102,2104,2106],{"class":106,"line":2099},49,[104,2101,1532],{"class":1531},[104,2103,1535],{"class":120},[104,2105,1538],{"class":1531},[104,2107,194],{"class":120},[104,2109,2111,2113,2115,2117,2119],{"class":106,"line":2110},50,[104,2112,1573],{"class":1531},[104,2114,1535],{"class":120},[104,2116,1688],{"class":1531},[104,2118,1553],{"class":116},[104,2120,194],{"class":120},[104,2122,2124,2126,2128,2130,2132],{"class":106,"line":2123},51,[104,2125,1683],{"class":1531},[104,2127,1535],{"class":120},[104,2129,1688],{"class":1531},[104,2131,1553],{"class":116},[104,2133,194],{"class":120},[104,2135,2137,2140,2142,2145,2147],{"class":106,"line":2136},52,[104,2138,2139],{"class":1531}," bottom",[104,2141,1535],{"class":120},[104,2143,2144],{"class":1531},"16",[104,2146,1553],{"class":116},[104,2148,194],{"class":120},[104,2150,2152,2155,2157,2159,2161],{"class":106,"line":2151},53,[104,2153,2154],{"class":1531}," right",[104,2156,1535],{"class":120},[104,2158,2144],{"class":1531},[104,2160,1553],{"class":116},[104,2162,194],{"class":120},[104,2164,2166],{"class":106,"line":2165},54,[104,2167,313],{"class":120},[104,2169,2171],{"class":106,"line":2170},55,[104,2172,2173],{"class":110},"/* finally position the views themselves */\n",[104,2175,2177,2179,2182],{"class":106,"line":2176},56,[104,2178,1384],{"class":1203},[104,2180,2181],{"class":130},".view",[104,2183,297],{"class":120},[104,2185,2187,2189,2191,2193],{"class":106,"line":2186},57,[104,2188,1532],{"class":1531},[104,2190,1535],{"class":120},[104,2192,1638],{"class":1531},[104,2194,194],{"class":120},[104,2196,2198,2200,2202,2204,2206],{"class":106,"line":2197},58,[104,2199,1545],{"class":1531},[104,2201,1535],{"class":120},[104,2203,1688],{"class":1531},[104,2205,1553],{"class":116},[104,2207,194],{"class":120},[104,2209,2211,2213,2215,2217,2219],{"class":106,"line":2210},59,[104,2212,1560],{"class":1531},[104,2214,1535],{"class":120},[104,2216,1550],{"class":1531},[104,2218,1553],{"class":116},[104,2220,194],{"class":120},[104,2222,2224,2226,2228,2230,2232],{"class":106,"line":2223},60,[104,2225,1573],{"class":1531},[104,2227,1535],{"class":120},[104,2229,1578],{"class":1531},[104,2231,1553],{"class":116},[104,2233,194],{"class":120},[104,2235,2237,2239,2241,2244,2246],{"class":106,"line":2236},61,[104,2238,1683],{"class":1531},[104,2240,1535],{"class":120},[104,2242,2243],{"class":1531},"396",[104,2245,1553],{"class":116},[104,2247,194],{"class":120},[104,2249,2251],{"class":106,"line":2250},62,[104,2252,313],{"class":120},[18,2254,2255],{},"This can act as a simple framework for an application with multiple views. Now we still need to make our navigation\ncontroller to switch between the different views. jQuery to the rescue — a small snippet initializes our view hierarchy\nand provides switching capabilities:",[95,2257,2259],{"className":97,"code":2258,"language":99,"meta":26,"style":26},"$(document).ready(function () {\n var navitems = $(\"#navigation li a\");\n navitems.click(function () {\n navitems.removeClass(\"current\");\n var ref = $(this).addClass(\"current\").attr(\"href\");\n /* hide the other views, show the one navigated to\n and trigger a custom event */\n $(\"div.view\").hide();\n $(ref).show().trigger(\"becameActive\");\n });\n $(\"div.view\").hide();\n $(\"div.view.current\").show().trigger(\"becameActive\");\n});\n",[101,2260,2261,2280,2300,2314,2328,2367,2372,2377,2394,2416,2421,2436,2459],{"__ignoreMap":26},[104,2262,2263,2266,2269,2272,2274,2277],{"class":106,"line":107},[104,2264,2265],{"class":130},"$",[104,2267,2268],{"class":120},"(document).",[104,2270,2271],{"class":130},"ready",[104,2273,217],{"class":120},[104,2275,2276],{"class":116},"function",[104,2278,2279],{"class":120}," () {\n",[104,2281,2282,2285,2288,2290,2293,2295,2298],{"class":106,"line":27},[104,2283,2284],{"class":116}," var",[104,2286,2287],{"class":120}," navitems ",[104,2289,124],{"class":116},[104,2291,2292],{"class":130}," $",[104,2294,217],{"class":120},[104,2296,2297],{"class":190},"\"#navigation li a\"",[104,2299,223],{"class":120},[104,2301,2302,2305,2308,2310,2312],{"class":106,"line":137},[104,2303,2304],{"class":120}," navitems.",[104,2306,2307],{"class":130},"click",[104,2309,217],{"class":120},[104,2311,2276],{"class":116},[104,2313,2279],{"class":120},[104,2315,2316,2319,2322,2324,2326],{"class":106,"line":161},[104,2317,2318],{"class":120}," navitems.",[104,2320,2321],{"class":130},"removeClass",[104,2323,217],{"class":120},[104,2325,1236],{"class":190},[104,2327,223],{"class":120},[104,2329,2330,2333,2336,2338,2340,2342,2345,2348,2351,2353,2355,2357,2360,2362,2365],{"class":106,"line":174},[104,2331,2332],{"class":116}," var",[104,2334,2335],{"class":120}," ref ",[104,2337,124],{"class":116},[104,2339,2292],{"class":130},[104,2341,217],{"class":120},[104,2343,2344],{"class":1531},"this",[104,2346,2347],{"class":120},").",[104,2349,2350],{"class":130},"addClass",[104,2352,217],{"class":120},[104,2354,1236],{"class":190},[104,2356,2347],{"class":120},[104,2358,2359],{"class":130},"attr",[104,2361,217],{"class":120},[104,2363,2364],{"class":190},"\"href\"",[104,2366,223],{"class":120},[104,2368,2369],{"class":106,"line":180},[104,2370,2371],{"class":110}," /* hide the other views, show the one navigated to\n",[104,2373,2374],{"class":106,"line":197},[104,2375,2376],{"class":110}," and trigger a custom event */\n",[104,2378,2379,2382,2384,2387,2389,2392],{"class":106,"line":209},[104,2380,2381],{"class":130}," $",[104,2383,217],{"class":120},[104,2385,2386],{"class":190},"\"div.view\"",[104,2388,2347],{"class":120},[104,2390,2391],{"class":130},"hide",[104,2393,134],{"class":120},[104,2395,2396,2398,2401,2404,2406,2409,2411,2414],{"class":106,"line":226},[104,2397,2381],{"class":130},[104,2399,2400],{"class":120},"(ref).",[104,2402,2403],{"class":130},"show",[104,2405,153],{"class":120},[104,2407,2408],{"class":130},"trigger",[104,2410,217],{"class":120},[104,2412,2413],{"class":190},"\"becameActive\"",[104,2415,223],{"class":120},[104,2417,2418],{"class":106,"line":253},[104,2419,2420],{"class":120}," });\n",[104,2422,2423,2426,2428,2430,2432,2434],{"class":106,"line":277},[104,2424,2425],{"class":130}," $",[104,2427,217],{"class":120},[104,2429,2386],{"class":190},[104,2431,2347],{"class":120},[104,2433,2391],{"class":130},[104,2435,134],{"class":120},[104,2437,2438,2440,2442,2445,2447,2449,2451,2453,2455,2457],{"class":106,"line":288},[104,2439,2425],{"class":130},[104,2441,217],{"class":120},[104,2443,2444],{"class":190},"\"div.view.current\"",[104,2446,2347],{"class":120},[104,2448,2403],{"class":130},[104,2450,153],{"class":120},[104,2452,2408],{"class":130},[104,2454,217],{"class":120},[104,2456,2413],{"class":190},[104,2458,223],{"class":120},[104,2460,2461],{"class":106,"line":300},[104,2462,2463],{"class":120},"});\n",[18,2465,2466,2467,2470,2471,2474],{},"With custom events like ",[101,2468,2469],{},"becameActive"," it is easy to create new callbacks that are invoked when state changes occur — in\nthis example when switching the active view through our navigation controller. For brevity we will omit a elaborate\nexample of controller code, but if for example you want code to run when switching to the ",[101,2472,2473],{},"#new"," view, you could simply\nwrite:",[95,2476,2478],{"className":97,"code":2477,"language":99,"meta":26,"style":26},"$(\"#new\").bind(\"becameActive\", function (event) {\n $(event.target).doSomething();\n});\n",[101,2479,2480,2511,2523],{"__ignoreMap":26},[104,2481,2482,2484,2486,2488,2490,2493,2495,2497,2500,2502,2505,2508],{"class":106,"line":107},[104,2483,2265],{"class":130},[104,2485,217],{"class":120},[104,2487,1273],{"class":190},[104,2489,2347],{"class":120},[104,2491,2492],{"class":130},"bind",[104,2494,217],{"class":120},[104,2496,2413],{"class":190},[104,2498,2499],{"class":120},", ",[104,2501,2276],{"class":116},[104,2503,2504],{"class":120}," (",[104,2506,2507],{"class":1773},"event",[104,2509,2510],{"class":120},") {\n",[104,2512,2513,2515,2518,2521],{"class":106,"line":27},[104,2514,2425],{"class":130},[104,2516,2517],{"class":120},"(event.target).",[104,2519,2520],{"class":130},"doSomething",[104,2522,134],{"class":120},[104,2524,2525],{"class":106,"line":137},[104,2526,2463],{"class":120},[18,2528,2529],{},"So what is still missing now? We still haven’t provided a data model to operate on. While data provided via web services\nis not a big deal by the use of JSON-APIs, we would certainly want to have a local data storage too. For quite some\ntime already, web engines provide a local SQLite-based storage system. On application initialization we can simply\nsetup some tables to hold our data and send statements from anywhere in the application later on:",[95,2531,2533],{"className":97,"code":2532,"language":99,"meta":26,"style":26},"// open a database, providing it's name, version,\n// maximum size and display name\nvar database = openDatabase(\"Example\", \"1.0\", 1048576, \"Example Database\");\ndatabase.transaction(function (transaction) {\n transaction.executeSQL(\n \"CREATE TABLE IF NOT EXISTS data\" +\n \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\" +\n \"... more declarations ...);\",\n );\n});\n// other statements are issued the same way\ndatabase.transaction(function (transaction) {\n transaction.executeSQL(\"SELECT * FROM data;\", function (transaction, result) {\n // do something with 'result'\n });\n});\n",[101,2534,2535,2540,2545,2579,2597,2608,2616,2623,2631,2636,2640,2645,2661,2687,2692,2696],{"__ignoreMap":26},[104,2536,2537],{"class":106,"line":107},[104,2538,2539],{"class":110},"// open a database, providing it's name, version,\n",[104,2541,2542],{"class":106,"line":27},[104,2543,2544],{"class":110},"// maximum size and display name\n",[104,2546,2547,2549,2552,2554,2557,2559,2562,2564,2567,2569,2572,2574,2577],{"class":106,"line":137},[104,2548,117],{"class":116},[104,2550,2551],{"class":120}," database ",[104,2553,124],{"class":116},[104,2555,2556],{"class":130}," openDatabase",[104,2558,217],{"class":120},[104,2560,2561],{"class":190},"\"Example\"",[104,2563,2499],{"class":120},[104,2565,2566],{"class":190},"\"1.0\"",[104,2568,2499],{"class":120},[104,2570,2571],{"class":1531},"1048576",[104,2573,2499],{"class":120},[104,2575,2576],{"class":190},"\"Example Database\"",[104,2578,223],{"class":120},[104,2580,2581,2584,2587,2589,2591,2593,2595],{"class":106,"line":161},[104,2582,2583],{"class":120},"database.",[104,2585,2586],{"class":130},"transaction",[104,2588,217],{"class":120},[104,2590,2276],{"class":116},[104,2592,2504],{"class":120},[104,2594,2586],{"class":1773},[104,2596,2510],{"class":120},[104,2598,2599,2602,2605],{"class":106,"line":174},[104,2600,2601],{"class":120}," transaction.",[104,2603,2604],{"class":130},"executeSQL",[104,2606,2607],{"class":120},"(\n",[104,2609,2610,2613],{"class":106,"line":180},[104,2611,2612],{"class":190}," \"CREATE TABLE IF NOT EXISTS data\"",[104,2614,2615],{"class":116}," +\n",[104,2617,2618,2621],{"class":106,"line":197},[104,2619,2620],{"class":190}," \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"",[104,2622,2615],{"class":116},[104,2624,2625,2628],{"class":106,"line":209},[104,2626,2627],{"class":190}," \"... more declarations ...);\"",[104,2629,2630],{"class":120},",\n",[104,2632,2633],{"class":106,"line":226},[104,2634,2635],{"class":120}," );\n",[104,2637,2638],{"class":106,"line":253},[104,2639,2463],{"class":120},[104,2641,2642],{"class":106,"line":277},[104,2643,2644],{"class":110},"// other statements are issued the same way\n",[104,2646,2647,2649,2651,2653,2655,2657,2659],{"class":106,"line":288},[104,2648,2583],{"class":120},[104,2650,2586],{"class":130},[104,2652,217],{"class":120},[104,2654,2276],{"class":116},[104,2656,2504],{"class":120},[104,2658,2586],{"class":1773},[104,2660,2510],{"class":120},[104,2662,2663,2665,2667,2669,2672,2674,2676,2678,2680,2682,2685],{"class":106,"line":300},[104,2664,2601],{"class":120},[104,2666,2604],{"class":130},[104,2668,217],{"class":120},[104,2670,2671],{"class":190},"\"SELECT * FROM data;\"",[104,2673,2499],{"class":120},[104,2675,2276],{"class":116},[104,2677,2504],{"class":120},[104,2679,2586],{"class":1773},[104,2681,2499],{"class":120},[104,2683,2684],{"class":1773},"result",[104,2686,2510],{"class":120},[104,2688,2689],{"class":106,"line":310},[104,2690,2691],{"class":110}," // do something with 'result'\n",[104,2693,2694],{"class":106,"line":1658},[104,2695,2420],{"class":120},[104,2697,2698],{"class":106,"line":1669},[104,2699,2463],{"class":120},[572,2701,2703],{"id":2702},"putting-it-all-together","Putting it all together",[18,2705,2706,2707,2710,2711],{},"A question remains: ",[318,2708,2709],{},"when this is the basic skeleton of an application, how do i put this on an actual\ndevice?"," ",[21,2712],{"alt":26,"src":2713},"https://media.synyx.de/uploads//2010/08/jquery-html-css-example-e1281692547570.png",[18,2715,2716],{},"Example in Simulator",[18,2718,2719,2720,2723],{},"While this could be simply served by a web server to a mobile device, the main reason for developing applications\ninstead of websites is the ability to expose them through an application store (e.g. Apples AppStore or the Android\nMarket). We won’t go into much detail here now — this is something to be discussed in the following blog posts in this\nseries — a simple answer is provided by a small framework\nnamed ",[60,2721,611],{"href":609,"rel":2722},[64],". PhoneGap provides the developer with\na cross-platform deployment environment, which makes use of the web engines on the different mobile devices. It\nbasically is a fullscreen web browser view, without any UI elements and other decorations but with added support for\naccessing hardware features of the actual device. Next to the already mentioned features, which are part of the modern\nweb engines, it gives access to features like cameras, accelerometer or access to the native phonebook of your mobile\nphone. You simply drop all your HTML, CSS & JavaScript into a PhoneGap application package which is built for your\nparticular device and are ready to deploy through any application store or similar channel you like.",[572,2725,2727],{"id":2726},"whats-next","What’s next?",[18,2729,2730],{},"So developing mobile applications with HTML, CSS & JavaScript is actually easy, isn’t it? Well, with the basic ideas\noutlined above it is — until it isn’t anymore. In the parts of this series which are still to come, we will answer some\nquestions, that might be crucial to your development cycle:",[1204,2732,2733,2736,2739,2742],{},[1223,2734,2735],{},"What problems might arise with JavaScript as a development language? Is there enough tool support for ease of\ndevelopment?",[1223,2737,2738],{},"What do i have to program from scratch and which frameworks are available, that erase my need of boilerplate code?",[1223,2740,2741],{},"What about the performance, for example when sophisticated animations are desired?",[1223,2743,2744],{},"Is my application really cross-platform, when using these technologies, and does it behave exactly the same on every\ndevice?",[18,2746,2747],{},"Stay tuned for more.",[352,2749,2750],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":26,"searchDepth":27,"depth":27,"links":2752},[2753,2754,2755,2756],{"id":1172,"depth":27,"text":1173},{"id":1179,"depth":27,"text":1180},{"id":2702,"depth":27,"text":2703},{"id":2726,"depth":27,"text":2727},[30,358],"2010-08-13T12:17:29","Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\\ndeployment target for mobile applications. That’s the theory at least. In this series of articles we will provide an\\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\\nseveral mobile platforms at once.","https://synyx.de/blog/on-cross-device-mobile-development-part-1/",{},"/blog/on-cross-device-mobile-development-part-1",{"title":1156,"description":2764},"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\ndeployment target for mobile applications. That’s the theory at least. In this series of articles we will provide an\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\nseveral mobile platforms at once.","blog/on-cross-device-mobile-development-part-1",[452,832,833,43,99],"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team of developers might be small or knowledge about…","yJGRhWop8a6UwlupSTSgkogMBabnFF7yVveZDcNWM8Y",{"id":2770,"title":2771,"author":2772,"body":2773,"category":2784,"date":2785,"description":26,"extension":33,"link":2786,"meta":2787,"navigation":36,"path":2788,"seo":2789,"slug":2777,"stem":2790,"tags":2791,"teaser":2792,"__hash__":2793},"blog/blog/human-maier-we-are-in-beta.md","Human Maier! We are in Beta!",[9],{"type":11,"value":2774,"toc":2782},[2775,2778],[14,2776,2771],{"id":2777},"human-maier-we-are-in-beta",[18,2779,2780],{},[21,2781],{"alt":23,"src":413},{"title":26,"searchDepth":27,"depth":27,"links":2783},[],[30,31],"2010-07-30T14:30:44","https://synyx.de/blog/human-maier-we-are-in-beta/",{},"/blog/human-maier-we-are-in-beta",{"title":2771,"description":26},"blog/human-maier-we-are-in-beta",[452,42,43],"Our little pet project “I think I spider” is almost ready to be thrown out into the wild! That means we are running a closed beta for iOS devices for…","Tv3D01DCynO0dlp6K4oLCcEF6qiHcJkQCehI25ZSfc0",{"id":2795,"title":2796,"author":2797,"body":2799,"category":3115,"date":3116,"description":3117,"extension":33,"link":3118,"meta":3119,"navigation":36,"path":3120,"seo":3121,"slug":2803,"stem":3123,"tags":3124,"teaser":3126,"__hash__":3127},"blog/blog/sending-apple-push-notifications-with-notnoops-java-apns-library.md","Sending Apple Push Notifications with notnoop's java-apns library",[2798],"knell",{"type":11,"value":2800,"toc":3113},[2801,2804,2818,2827,2830,2837,2840,3108,3111],[14,2802,2796],{"id":2803},"sending-apple-push-notifications-with-notnoops-java-apns-library",[18,2805,2806,2807,2811,2812,2817],{},"If you need to send apple push notifications to your users, like we do in\na ",[60,2808,2810],{"href":485,"rel":2809},[64],"secret project"," mentioned earlier this\nweek, ",[60,2813,2816],{"href":2814,"rel":2815},"http://github.com/notnoop/java-apns",[64],"notnoop’s java-apns library"," is a good choice, because its really simple to\nuse and saves you a lot of work.",[18,2819,2820,2821,2826],{},"(I presume that you already know how to get the Tokens of your users and already have a certificate for the push\nnotifications, I only show you how easy the server part can be with this library. If you don’t know, read this\nfirst: ",[60,2822,2825],{"href":2823,"rel":2824},"http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html",[64],"Apple Push Service",")",[18,2828,2829],{},"First you have to download the library (or add it in your maven dependencies):",[18,2831,2832],{},[60,2833,2836],{"href":2834,"rel":2835},"http://github.com/notnoop/java-apns/tree/",[64],"java-apns",[18,2838,2839],{},"The programming part isn’t that much so here’s how you do it:",[95,2841,2845],{"className":2842,"code":2843,"language":2844,"meta":26,"style":26},"language-java shiki shiki-themes github-light github-dark","\npublic void pushMessage() {\n ApnsService service = null;\n try {\n // get the certificate\n InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(\"your_certificate.p12\");\n service = APNS.newService().withCert(certStream, \"your_cert_password\").withSandboxDestination().build();\n // or\n // service = APNS.newService().withCert(certStream,\n // \"your_cert_password\").withProductionDestination().build();\n service.start();\n // You have to delete the devices from you list that no longer\n //have the app installed, see method below\n deleteInactiveDevices(service);\n // read your user list\n List\u003CUser> userList = userDao.readUsers();\n for (User user : userList) {\n try {\n // we had a daily update here, so we need to know how many\n //days the user hasn't started the app\n // so that we get the number of updates to display it as the badge.\n int days = (int) ((System.currentTimeMillis() - user.getLastUpdate()) / 1000 / 60 / 60 / 24);\n PayloadBuilder payloadBuilder = APNS.newPayload();\n payloadBuilder = payloadBuilder.badge(days).alertBody(\"some message you want to send here\");\n // check if the message is too long (it won't be sent if it is)\n //and trim it if it is.\n if (payloadBuilder.isTooLong()) {\n payloadBuilder = payloadBuilder.shrinkBody();\n }\n String payload = payloadBuilder.build();\n String token = user.getToken();\n service.push(token, payload);\n } catch (Exception ex) {\n // some logging stuff\n }\n }\n } catch (Exception ex) {\n // more logging\n } finally {\n // check if the service was successfull initialized and stop it here, if it was\n if (service != null) {\n service.stop();\n }\n }\n }\n private void deleteInactiveDevices(ApnsService service) {\n // get the list of the devices that no longer have your app installed from apple\n //ignore the =\"\" after Date here, it's a bug...\n Map\u003CString, Date> inactiveDevices = service.getInactiveDevices();\n for (String deviceToken : inactiveDevices.keySet()) {\n userDao.deleteByDeviceId(deviceToken);\n }\n }\n\n","java",[101,2846,2847,2851,2856,2861,2866,2871,2876,2881,2886,2891,2896,2901,2906,2911,2916,2921,2926,2931,2936,2941,2946,2951,2956,2961,2966,2971,2976,2981,2986,2991,2996,3001,3006,3011,3016,3021,3026,3031,3036,3041,3046,3051,3056,3060,3065,3070,3075,3080,3085,3090,3095,3100,3104],{"__ignoreMap":26},[104,2848,2849],{"class":106,"line":107},[104,2850,1039],{"emptyLinePlaceholder":36},[104,2852,2853],{"class":106,"line":27},[104,2854,2855],{},"public void pushMessage() {\n",[104,2857,2858],{"class":106,"line":137},[104,2859,2860],{}," ApnsService service = null;\n",[104,2862,2863],{"class":106,"line":161},[104,2864,2865],{}," try {\n",[104,2867,2868],{"class":106,"line":174},[104,2869,2870],{}," // get the certificate\n",[104,2872,2873],{"class":106,"line":180},[104,2874,2875],{}," InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(\"your_certificate.p12\");\n",[104,2877,2878],{"class":106,"line":197},[104,2879,2880],{}," service = APNS.newService().withCert(certStream, \"your_cert_password\").withSandboxDestination().build();\n",[104,2882,2883],{"class":106,"line":209},[104,2884,2885],{}," // or\n",[104,2887,2888],{"class":106,"line":226},[104,2889,2890],{}," // service = APNS.newService().withCert(certStream,\n",[104,2892,2893],{"class":106,"line":253},[104,2894,2895],{}," // \"your_cert_password\").withProductionDestination().build();\n",[104,2897,2898],{"class":106,"line":277},[104,2899,2900],{}," service.start();\n",[104,2902,2903],{"class":106,"line":288},[104,2904,2905],{}," // You have to delete the devices from you list that no longer\n",[104,2907,2908],{"class":106,"line":300},[104,2909,2910],{}," //have the app installed, see method below\n",[104,2912,2913],{"class":106,"line":310},[104,2914,2915],{}," deleteInactiveDevices(service);\n",[104,2917,2918],{"class":106,"line":1658},[104,2919,2920],{}," // read your user list\n",[104,2922,2923],{"class":106,"line":1669},[104,2924,2925],{}," List\u003CUser> userList = userDao.readUsers();\n",[104,2927,2928],{"class":106,"line":1680},[104,2929,2930],{}," for (User user : userList) {\n",[104,2932,2933],{"class":106,"line":1695},[104,2934,2935],{}," try {\n",[104,2937,2938],{"class":106,"line":1709},[104,2939,2940],{}," // we had a daily update here, so we need to know how many\n",[104,2942,2943],{"class":106,"line":1722},[104,2944,2945],{}," //days the user hasn't started the app\n",[104,2947,2948],{"class":106,"line":1737},[104,2949,2950],{}," // so that we get the number of updates to display it as the badge.\n",[104,2952,2953],{"class":106,"line":1742},[104,2954,2955],{}," int days = (int) ((System.currentTimeMillis() - user.getLastUpdate()) / 1000 / 60 / 60 / 24);\n",[104,2957,2958],{"class":106,"line":1760},[104,2959,2960],{}," PayloadBuilder payloadBuilder = APNS.newPayload();\n",[104,2962,2963],{"class":106,"line":1779},[104,2964,2965],{}," payloadBuilder = payloadBuilder.badge(days).alertBody(\"some message you want to send here\");\n",[104,2967,2968],{"class":106,"line":1792},[104,2969,2970],{}," // check if the message is too long (it won't be sent if it is)\n",[104,2972,2973],{"class":106,"line":1797},[104,2974,2975],{}," //and trim it if it is.\n",[104,2977,2978],{"class":106,"line":1818},[104,2979,2980],{}," if (payloadBuilder.isTooLong()) {\n",[104,2982,2983],{"class":106,"line":1834},[104,2984,2985],{}," payloadBuilder = payloadBuilder.shrinkBody();\n",[104,2987,2988],{"class":106,"line":1839},[104,2989,2990],{}," }\n",[104,2992,2993],{"class":106,"line":1854},[104,2994,2995],{}," String payload = payloadBuilder.build();\n",[104,2997,2998],{"class":106,"line":1870},[104,2999,3000],{}," String token = user.getToken();\n",[104,3002,3003],{"class":106,"line":1883},[104,3004,3005],{}," service.push(token, payload);\n",[104,3007,3008],{"class":106,"line":1888},[104,3009,3010],{}," } catch (Exception ex) {\n",[104,3012,3013],{"class":106,"line":1907},[104,3014,3015],{}," // some logging stuff\n",[104,3017,3018],{"class":106,"line":1923},[104,3019,3020],{}," }\n",[104,3022,3023],{"class":106,"line":1928},[104,3024,3025],{}," }\n",[104,3027,3028],{"class":106,"line":1943},[104,3029,3030],{}," } catch (Exception ex) {\n",[104,3032,3033],{"class":106,"line":1959},[104,3034,3035],{}," // more logging\n",[104,3037,3038],{"class":106,"line":1973},[104,3039,3040],{}," } finally {\n",[104,3042,3043],{"class":106,"line":1978},[104,3044,3045],{}," // check if the service was successfull initialized and stop it here, if it was\n",[104,3047,3048],{"class":106,"line":1997},[104,3049,3050],{}," if (service != null) {\n",[104,3052,3053],{"class":106,"line":2013},[104,3054,3055],{}," service.stop();\n",[104,3057,3058],{"class":106,"line":2018},[104,3059,3025],{},[104,3061,3062],{"class":106,"line":2033},[104,3063,3064],{}," }\n",[104,3066,3067],{"class":106,"line":2049},[104,3068,3069],{}," }\n",[104,3071,3072],{"class":106,"line":2063},[104,3073,3074],{}," private void deleteInactiveDevices(ApnsService service) {\n",[104,3076,3077],{"class":106,"line":2068},[104,3078,3079],{}," // get the list of the devices that no longer have your app installed from apple\n",[104,3081,3082],{"class":106,"line":2083},[104,3083,3084],{}," //ignore the =\"\" after Date here, it's a bug...\n",[104,3086,3087],{"class":106,"line":2099},[104,3088,3089],{}," Map\u003CString, Date> inactiveDevices = service.getInactiveDevices();\n",[104,3091,3092],{"class":106,"line":2110},[104,3093,3094],{}," for (String deviceToken : inactiveDevices.keySet()) {\n",[104,3096,3097],{"class":106,"line":2123},[104,3098,3099],{}," userDao.deleteByDeviceId(deviceToken);\n",[104,3101,3102],{"class":106,"line":2136},[104,3103,3064],{},[104,3105,3106],{"class":106,"line":2151},[104,3107,3069],{},[18,3109,3110],{},"Now wasn’t that an easy one this time?",[352,3112,930],{},{"title":26,"searchDepth":27,"depth":27,"links":3114},[],[30],"2010-07-27T07:00:38","If you need to send apple push notifications to your users, like we do in\\na secret project mentioned earlier this\\nweek, notnoop’s java-apns library is a good choice, because its really simple to\\nuse and saves you a lot of work.","https://synyx.de/blog/sending-apple-push-notifications-with-notnoops-java-apns-library/",{},"/blog/sending-apple-push-notifications-with-notnoops-java-apns-library",{"title":2796,"description":3122},"If you need to send apple push notifications to your users, like we do in\na secret project mentioned earlier this\nweek, notnoop’s java-apns library is a good choice, because its really simple to\nuse and saves you a lot of work.","blog/sending-apple-push-notifications-with-notnoops-java-apns-library",[424,42,43,3125],"push-notification","If you need to send apple push notifications to your users, like we do in a secret project mentioned earlier this week, notnoop’s java-apns library is a good choice, because…","mqOSmpufU8Sqpzy9fxCtmH5D6V8CLhPDD5kkCRFFWO8",{"id":3129,"title":969,"author":3130,"body":3131,"category":3141,"date":3142,"description":26,"extension":33,"link":3143,"meta":3144,"navigation":36,"path":3145,"seo":3146,"slug":1011,"stem":3147,"tags":3148,"teaser":3149,"__hash__":3150},"blog/blog/i-think-i-spider.md",[9],{"type":11,"value":3132,"toc":3139},[3133,3135],[14,3134,969],{"id":1011},[18,3136,3137],{},[21,3138],{"alt":23,"src":413},{"title":26,"searchDepth":27,"depth":27,"links":3140},[],[30,31],"2010-07-26T06:56:04","https://synyx.de/blog/i-think-i-spider/",{},"/blog/i-think-i-spider",{"title":969,"description":26},"blog/i-think-i-spider",[452,43],"We are working on a fair number of Apps here at our mobile team and today we are proud to announce one of them: I think I spider! The “I…","Q4uy3XPVMpWwUGobhts0mEDqawiYKLMPBPP9qYTF_us",{"id":3152,"title":459,"author":3153,"body":3154,"category":3236,"date":3237,"description":3238,"extension":33,"link":3239,"meta":3240,"navigation":36,"path":3241,"seo":3242,"slug":3244,"stem":3245,"tags":3246,"teaser":3247,"__hash__":3248},"blog/blog/mobile-solutions-summary-4.md",[9],{"type":11,"value":3155,"toc":3234},[3156,3158,3172,3192,3200,3215,3220],[14,3157,459],{"id":465},[18,3159,3160,3161,3165,3166,3171],{},"There’s a lot going on over at the ",[60,3162,479],{"href":3163,"rel":3164},"http://mobile.synyx.de",[64],", so in case you are not subscribed to\nour feed, which I hope you are, you can grab it ",[60,3167,3170],{"href":3168,"rel":3169},"http://mobile.synyx.de/feed/",[64],"here",". In order to convince you to hook\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.",[18,3173,3174,3179,3180,3185,3186,3191],{},[60,3175,3178],{"href":3176,"rel":3177},"http://mobile.synyx.de/authors/?uid=3",[64],"Tobias’","\npost ",[60,3181,3184],{"href":3182,"rel":3183},"http://mobile.synyx.de/2010/06/android-and-self-signed-ssl-certificates/",[64],"“Android and self-signed ssl certificates”","\ngained a lot of attraction over the past couple of weeks. He basically brings you up to speed on how to tweak Android’s\nversion of ",[60,3187,3190],{"href":3188,"rel":3189},"http://hc.apache.org/httpcomponents-client/httpclient/",[64],"Apache Commons Http"," to work with your own\ncertificate:",[490,3193,3194,3197],{},[18,3195,3196],{},"Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and\nlet android accept them.",[18,3198,3199],{},"But fortunately, there’s a workaround that uses an own SSLSocketFactory and an own TrustManager. With this, only your\nadded site is beeing able to be called, so theres no security issue.",[18,3201,3202,3203,3208,3209,3214],{},"Another blog post I’d like to point out is by yours truly,\non ",[60,3204,3207],{"href":3205,"rel":3206},"http://mobile.synyx.de/2010/06/ui-prototyping-iphone-apps/",[64],"“UI Prototyping iPhone Apps”",". It covers a very simple\nconcept and provides you with a ",[60,3210,3213],{"href":3211,"rel":3212},"http://github.com/dlinsin/district9/tree/master/UIPrototyping/",[64],"framework"," to employ it\nin your App development:",[490,3216,3217],{},[18,3218,3219],{},"I watched a whole bunch of sessions from 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret\nVictor… In his session, Bret shows how to prototype an interface only by using with screenshots! … It inspired me to use\nhis framework and the whole process for our own development… Unfortunately, the code for the session isn’t available …\nAfter some digging, I found Michael Fey’s blog, who was able to successfully reverse engineer the missing parts, which\nwere not shown in the presentation.",[18,3221,3222,3223,3227,3228,3233],{},"I hope by this time you have already subscribed to our ",[60,3224,3226],{"href":3163,"rel":3225},[64],"mobile blog"," and discovered a couple\nof ",[60,3229,3232],{"href":3230,"rel":3231},"http://mobile.synyx.de/2010/07/split-nsstring-by-characters/",[64],"interesting posts",", that our team put together over\npast couple of months.",{"title":26,"searchDepth":27,"depth":27,"links":3235},[],[534],"2010-07-16T07:28:03","There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to\\nour feed, which I hope you are, you can grab it here. In order to convince you to hook\\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.","https://synyx.de/blog/mobile-solutions-summary-4/",{},"/blog/mobile-solutions-summary-4",{"title":459,"description":3243},"There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to\nour feed, which I hope you are, you can grab it here. In order to convince you to hook\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.","mobile-solutions-summary-4","blog/mobile-solutions-summary-4",[452,42,43,545],"There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to our feed, which I hope you are, you can grab it…","6e3hlP9lM0lmBm1BXb-OBwSJM2DFtSNp5TIpHdJ1OXE",{"id":3250,"title":3251,"author":3252,"body":3253,"category":3492,"date":3493,"description":3494,"extension":33,"link":3495,"meta":3496,"navigation":36,"path":3497,"seo":3498,"slug":3257,"stem":3500,"tags":3501,"teaser":3504,"__hash__":3505},"blog/blog/ui-prototyping-iphone-apps.md","UI Prototyping iPhone Apps",[9],{"type":11,"value":3254,"toc":3490},[3255,3258,3266,3269,3278,3289,3342,3352,3475,3478,3481,3488],[14,3256,3251],{"id":3257},"ui-prototyping-iphone-apps",[18,3259,3260,3261,3265],{},"Before ",[60,3262,3264],{"href":62,"rel":3263},[64],"flying off to WWDC"," last month, I watched a whole bunch of sessions\nfrom 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you haven’t watched it and\nyou’ve got access to the WWDC videos – stop right here and watch the video!",[18,3267,3268],{},"In his session, Bret shows how to prototype an interface only by using with screenshots! It’s amazing that a simple\nscreenshot on the device can show you so much more than by just looking at it in a document or print out. It inspired me\nto use his framework and the whole process for our own development.",[18,3270,3271,3272,3277],{},"Unfortunately, the code for the session isn’t available and neither Bret nor the frameworks evangelist mentioned in the\npresentation got back to me about the code. After some digging, I\nfound ",[60,3273,3276],{"href":3274,"rel":3275},"https://web.archive.org/web/20130723100234/http://www.fruitstandsoftware.com:80/blog/2009/07/uiview-manipulation-made-easier-with-a-category/",[64],"Michael Fey’s blog",",\nwho was able to successfully reverse engineer the missing parts, which were not shown in the presentation.",[18,3279,3280,3281,3284,3285,3288],{},"Michael’s ",[318,3282,3283],{},"UIViewAdditions"," basically allow easy access to frame properties and give you a neat init method, which adds\nthe passed ",[318,3286,3287],{},"UIView"," as a parent:",[95,3290,3292],{"className":875,"code":3291,"language":877,"meta":26,"style":26},"- (id)initWithParent:(UIView *)parent {\n self = [self initWithFrame:CGRectZero];\n if (!self)\n return nil;\n [parent addSubview:self];\n return self;\n}\n+ (id) viewWithParent:(UIView *)parent {\n return [[[self alloc] initWithParent:parent] autorelease];\n}\n",[101,3293,3294,3299,3304,3309,3314,3319,3324,3328,3333,3338],{"__ignoreMap":26},[104,3295,3296],{"class":106,"line":107},[104,3297,3298],{},"- (id)initWithParent:(UIView *)parent {\n",[104,3300,3301],{"class":106,"line":27},[104,3302,3303],{}," self = [self initWithFrame:CGRectZero];\n",[104,3305,3306],{"class":106,"line":137},[104,3307,3308],{}," if (!self)\n",[104,3310,3311],{"class":106,"line":161},[104,3312,3313],{}," return nil;\n",[104,3315,3316],{"class":106,"line":174},[104,3317,3318],{}," [parent addSubview:self];\n",[104,3320,3321],{"class":106,"line":180},[104,3322,3323],{}," return self;\n",[104,3325,3326],{"class":106,"line":197},[104,3327,313],{},[104,3329,3330],{"class":106,"line":209},[104,3331,3332],{},"+ (id) viewWithParent:(UIView *)parent {\n",[104,3334,3335],{"class":106,"line":226},[104,3336,3337],{}," return [[[self alloc] initWithParent:parent] autorelease];\n",[104,3339,3340],{"class":106,"line":253},[104,3341,313],{},[18,3343,3344,3345,3348,3349,3351],{},"There wasn’t much left to do for me. I only coded the class ",[318,3346,3347],{},"Root",", which is the parent of all ",[318,3350,320],{}," instances\nused in the prototype. It provides a couple of methods to slide images back and forth:",[95,3353,3355],{"className":875,"code":3354,"language":877,"meta":26,"style":26},"@synthesize pageIndex = _pageIndex;\n- (id) initWithParent:(UIView *)parent {\n self = [super initWithParent:parent];\n if (self == nil) {\n return nil;\n }\n self.userInteractionEnabled = YES;\n self.size = self.window.size;\n [[UIImageView viewWithParent:self] setImageWithName:@\"dailies\"];\n self.pageIndex = 0;\n return self;\n}\n- (void)setPageIndex:(int)index {\n if (index \u003C 0 || index >= [self.subviews count]) {\n return;\n }\n _pageIndex = index;\n for (int i = 0; i \u003C [self.subviews count]; i++) {\n UIImageView *page = [self.subviews objectAtIndex:i];\n page.x = (i \u003C _pageIndex) ? -self.width : (i > _pageIndex) ? self.width : 0;\n }\n}\n- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {\n self.pageIndex++;\n}\n",[101,3356,3357,3362,3367,3372,3377,3381,3386,3391,3396,3401,3406,3410,3414,3419,3424,3429,3433,3438,3443,3448,3453,3457,3461,3466,3471],{"__ignoreMap":26},[104,3358,3359],{"class":106,"line":107},[104,3360,3361],{},"@synthesize pageIndex = _pageIndex;\n",[104,3363,3364],{"class":106,"line":27},[104,3365,3366],{},"- (id) initWithParent:(UIView *)parent {\n",[104,3368,3369],{"class":106,"line":137},[104,3370,3371],{}," self = [super initWithParent:parent];\n",[104,3373,3374],{"class":106,"line":161},[104,3375,3376],{}," if (self == nil) {\n",[104,3378,3379],{"class":106,"line":174},[104,3380,3313],{},[104,3382,3383],{"class":106,"line":180},[104,3384,3385],{}," }\n",[104,3387,3388],{"class":106,"line":197},[104,3389,3390],{}," self.userInteractionEnabled = YES;\n",[104,3392,3393],{"class":106,"line":209},[104,3394,3395],{}," self.size = self.window.size;\n",[104,3397,3398],{"class":106,"line":226},[104,3399,3400],{}," [[UIImageView viewWithParent:self] setImageWithName:@\"dailies\"];\n",[104,3402,3403],{"class":106,"line":253},[104,3404,3405],{}," self.pageIndex = 0;\n",[104,3407,3408],{"class":106,"line":277},[104,3409,3323],{},[104,3411,3412],{"class":106,"line":288},[104,3413,313],{},[104,3415,3416],{"class":106,"line":300},[104,3417,3418],{},"- (void)setPageIndex:(int)index {\n",[104,3420,3421],{"class":106,"line":310},[104,3422,3423],{}," if (index \u003C 0 || index >= [self.subviews count]) {\n",[104,3425,3426],{"class":106,"line":1658},[104,3427,3428],{}," return;\n",[104,3430,3431],{"class":106,"line":1669},[104,3432,3385],{},[104,3434,3435],{"class":106,"line":1680},[104,3436,3437],{}," _pageIndex = index;\n",[104,3439,3440],{"class":106,"line":1695},[104,3441,3442],{}," for (int i = 0; i \u003C [self.subviews count]; i++) {\n",[104,3444,3445],{"class":106,"line":1709},[104,3446,3447],{}," UIImageView *page = [self.subviews objectAtIndex:i];\n",[104,3449,3450],{"class":106,"line":1722},[104,3451,3452],{}," page.x = (i \u003C _pageIndex) ? -self.width : (i > _pageIndex) ? self.width : 0;\n",[104,3454,3455],{"class":106,"line":1737},[104,3456,3385],{},[104,3458,3459],{"class":106,"line":1742},[104,3460,313],{},[104,3462,3463],{"class":106,"line":1760},[104,3464,3465],{},"- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {\n",[104,3467,3468],{"class":106,"line":1779},[104,3469,3470],{}," self.pageIndex++;\n",[104,3472,3473],{"class":106,"line":1792},[104,3474,313],{},[18,3476,3477],{},"With those two classes and a couple of screenshots, it is fairly easy to create an App that looks and feels almost real.\nI created a short demo video, which shows how easy it is to get a good feeling if your App is going to work or not:",[18,3479,3480],{},"Now don’t forget those are only screenshots and the App might need to load stuff over the network or do some animation,\nhence it might not feel exactly the same. However, this process of prototyping an UI is powerful enough to give you an\nidea, whether the workflow or the UI in general is going to “work” or needs some tweaking.",[18,3482,3483,3484,72],{},"You can download the source code for the two classes, along with a sample project\nfrom ",[60,3485,3487],{"href":3211,"rel":3486},[64],"github",[352,3489,930],{},{"title":26,"searchDepth":27,"depth":27,"links":3491},[],[30,358],"2010-06-29T06:46:00","Before flying off to WWDC last month, I watched a whole bunch of sessions\\nfrom 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you haven’t watched it and\\nyou’ve got access to the WWDC videos – stop right here and watch the video!","https://synyx.de/blog/ui-prototyping-iphone-apps/",{},"/blog/ui-prototyping-iphone-apps",{"title":3251,"description":3499},"Before flying off to WWDC last month, I watched a whole bunch of sessions\nfrom 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you haven’t watched it and\nyou’ve got access to the WWDC videos – stop right here and watch the video!","blog/ui-prototyping-iphone-apps",[42,3502,43,397,3503],"design","prototyping","Before flying off to WWDC last month, I watched a whole bunch of sessions from 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you…","hEZiAfMXrZSp1Im2rsk88x4HQuKpzIhV5W2xo11faBA",{"id":3507,"title":459,"author":3508,"body":3509,"category":3620,"date":3621,"description":3622,"extension":33,"link":3623,"meta":3624,"navigation":36,"path":3625,"seo":3626,"slug":3628,"stem":3629,"tags":3630,"teaser":3631,"__hash__":3632},"blog/blog/mobile-solutions-summary-3.md",[9],{"type":11,"value":3510,"toc":3618},[3511,3513,3533,3547,3585,3603,3610],[14,3512,459],{"id":465},[18,3514,3515,3516,3521,3522,3527,3528,3532],{},"In my ",[60,3517,3520],{"href":3518,"rel":3519},"http://blog.synyx.de/2010/05/mobile-solutions-%E2%80%93-summary/",[64],"last summary"," I forgot to mention, that I will be\nat ",[60,3523,3526],{"href":3524,"rel":3525},"http://developer.apple.com/wwdc",[64],"WWDC"," this year. Now WWDC is over, I just got back and if you want to know more,\ncheckout my blog post ",[60,3529,3170],{"href":3530,"rel":3531},"http://dlinsin.blogspot.com/2010/06/wwdc10.html",[64],". While I was gone, our team was busy\npublishing all sorts of good stuff!",[18,3534,3535,3536,3541,3542,3546],{},"Let’s start with the release of Android’s FroYo last\nmonth! ",[60,3537,3540],{"href":3538,"rel":3539},"http://mobile.synyx.de/2010/05/in-my-humble-opinion-froyo-rocks/",[64],"Achim"," wrote\na ",[60,3543,3545],{"href":3538,"rel":3544},[64],"nice blog post"," highlighting the noteworthy\nfeatures:",[490,3548,3549,3552,3555,3563,3566,3574,3577],{},[18,3550,3551],{},"*FroYo is like each previous version a mixture between API Changes, new Userfeatures and some new cool Apps….",[18,3553,3554],{},"New API-Features:*",[1204,3556,3557,3560],{},[1223,3558,3559],{},"Data Backup API",[1223,3561,3562],{},"Possibility to save passwords secure",[18,3564,3565],{},"New User Features:",[1204,3567,3568,3571],{},[1223,3569,3570],{},"updated Exchange Features",[1223,3572,3573],{},"Remote Wipe",[18,3575,3576],{},"New Apps:",[1204,3578,3579,3582],{},[1223,3580,3581],{},"Camera and Camcorder updated (possible to enable manually the leds for usage within camcorder)",[1223,3583,3584],{},"Android Tethering and Usage as Hotspot",[18,3586,3587,3591,3592,3597,3598,488],{},[60,3588,523],{"href":3589,"rel":3590},"http://mobile.synyx.de/authors/?uid=6",[64],", our ",[60,3593,3596],{"href":3594,"rel":3595},"http://mobile.synyx.de/tag/maemo-5/",[64],"Maemo"," guy, followed our\nGoogle Maps theme and published the first part of a nice introduction\nof ",[60,3599,3602],{"href":3600,"rel":3601},"http://mobile.synyx.de/2010/06/google-maps-on-maemo-5-part-1/",[64],"Google Maps on Maemo 5",[490,3604,3605],{},[18,3606,3607],{},[318,3608,3609],{},"The idea is quite simple. Webkit will render a webpage insider your app. That webpage consists of javascript methods\nwhich use the Google Maps-API. The javascript methods can be triggered from the app. The map class acts as proxy for\nthe communication between your app and the website. Quite simple hm?",[18,3611,3612,3613,3617],{},"This is only a small sneak peak of what’s going on over at the mobile solutions blog. I suggest\nyou ",[60,3614,3616],{"href":477,"rel":3615},[64],"add it"," to your favorite feed reader and check it out regularly.",{"title":26,"searchDepth":27,"depth":27,"links":3619},[],[534],"2010-06-18T08:57:09","In my last summary I forgot to mention, that I will be\\nat WWDC this year. Now WWDC is over, I just got back and if you want to know more,\\ncheckout my blog post here. While I was gone, our team was busy\\npublishing all sorts of good stuff!","https://synyx.de/blog/mobile-solutions-summary-3/",{},"/blog/mobile-solutions-summary-3",{"title":459,"description":3627},"In my last summary I forgot to mention, that I will be\nat WWDC this year. Now WWDC is over, I just got back and if you want to know more,\ncheckout my blog post here. While I was gone, our team was busy\npublishing all sorts of good stuff!","mobile-solutions-summary-3","blog/mobile-solutions-summary-3",[42,43,545],"In my last summary I forgot to mention, that I will be at WWDC this year. Now WWDC is over, I just got back and if you want to know…","Vt3IIuwgnXxkVeplkiSoihd83Xam9gAc_bmG2yhTjts",{"id":3634,"title":65,"author":3635,"body":3636,"category":3670,"date":3671,"description":3672,"extension":33,"link":3673,"meta":3674,"navigation":36,"path":3675,"seo":3676,"slug":3640,"stem":3678,"tags":3679,"teaser":3681,"__hash__":3682},"blog/blog/wwdc-2010.md",[9],{"type":11,"value":3637,"toc":3668},[3638,3641,3650,3665],[14,3639,65],{"id":3640},"wwdc-2010",[18,3642,3643,3644,3649],{},"I’m going to ",[60,3645,3648],{"href":3646,"rel":3647},"http://developer.apple.com/wwdc/",[64],"Apple’s WWDC"," this year and I’m pretty excited to fully dive into iPhone\ndevelopment for the whole next week. That it takes place in San Francisco is a nice bonus, as well.",[18,3651,3652,3653,3658,3659,3664],{},"The conference is ",[60,3654,3657],{"href":3655,"rel":3656},"http://developer.apple.com/wwdc/sessions/",[64],"mostly covering iPhone"," related topics. There\nare ",[60,3660,3663],{"href":3661,"rel":3662},"http://www.fscklog.com/2010/05/apple-best%C3%A4tigt-wwdc-keynote-mit-steve-jobs-am-7-juni.html",[64],"rumors"," that Steven Jobs\nwill present the new iPhone on Monday’s keynote. I’ll make sure I won’t miss that. My schedule for the week is already\nplanned with topics like game development, multitasking and user interface design.",[18,3666,3667],{},"If you are going to WWDC as well and would like to meet up, let me know.",{"title":26,"searchDepth":27,"depth":27,"links":3669},[],[30],"2010-06-03T15:12:08","I’m going to Apple’s WWDC this year and I’m pretty excited to fully dive into iPhone\\ndevelopment for the whole next week. That it takes place in San Francisco is a nice bonus, as well.","https://synyx.de/blog/wwdc-2010/",{},"/blog/wwdc-2010",{"title":65,"description":3677},"I’m going to Apple’s WWDC this year and I’m pretty excited to fully dive into iPhone\ndevelopment for the whole next week. That it takes place in San Francisco is a nice bonus, as well.","blog/wwdc-2010",[42,3680,43],"conference","I’m going to Apple’s WWDC this year and I’m pretty excited to fully dive into iPhone development for the whole next week. That it takes place in San Francisco is…","0PlPIuqAd36_OiWhK21AO20tc4c6Y1ucyQYMRpnc0aQ",{"id":3684,"title":3685,"author":3686,"body":3687,"category":3976,"date":3977,"description":3978,"extension":33,"link":3979,"meta":3980,"navigation":36,"path":3981,"seo":3982,"slug":3691,"stem":3984,"tags":3985,"teaser":3987,"__hash__":3988},"blog/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii.md","How to add a “Find Your Company” feature to your iPhone App – Part II",[9],{"type":11,"value":3688,"toc":3974},[3689,3692,3705,3708,3714,3717,3733,3900,3903,3909,3932,3939,3953,3960,3965,3972],[14,3690,3685],{"id":3691},"how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",[18,3693,3694,3695,3700,3701,72],{},"In\nmy ",[60,3696,3699],{"href":3697,"rel":3698},"http://mobile.synyx.de/2010/05/06/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i/",[64],"first installment",",\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\ndownload the code for this tutorial on ",[60,3702,3487],{"href":3703,"rel":3704},"http://gist.github.com/388323/ea35c6d31270babb73ec052f1442c43afd6b5510",[64],[18,3706,3707],{},"If you start the App where we left off, you get the two pins, but it won’t be properly zoomed. In fact, it would look\nsomething like this:",[18,3709,3710],{},[21,3711],{"alt":3712,"src":3713},"\"screen_synyx_map\"","https://media.synyx.de/uploads//2010/05/maps-3.png",[18,3715,3716],{},"As you can see, the pins are not really helpful at this zoom level and it’s definitely not user friendly, if you have to\nzoom in by yourself. What we need is a way to zoom in, so that the pin and the current location nicely fit on the\nscreen.",[18,3718,3719,3720,3723,3724,3728,3729,3732],{},"That’s exactly what the method ",[318,3721,3722],{},"centerMapAroundAnnotations"," at\nline ",[60,3725,3727],{"href":3703,"rel":3726},[64],"108"," of our ",[318,3730,3731],{},"UIViewController"," does:",[95,3734,3736],{"className":875,"code":3735,"language":877,"meta":26,"style":26},"\nif ( [[self.mapView annotations] count] \u003C 2 )\n return;\nCLLocationCoordinate2D min;\nCLLocationCoordinate2D max;\nBOOL minMaxInitialized = NO;\nfor ( id\u003CMKAnnotation> a in [self.mapView annotations] ) {\n if ( !minMaxInitialized ) {\n min = a.coordinate;\n max = a.coordinate;\n minMaxInitialized = YES;\n } else {\n min.latitude = MIN( min.latitude, a.coordinate.latitude );\n min.longitude = MIN( min.longitude, a.coordinate.longitude );\n max.latitude = MAX( max.latitude, a.coordinate.latitude );\n max.longitude = MAX( max.longitude, a.coordinate.longitude );\n }\n}\u003Cbr/>\nCLLocation* locSouthWest = [[CLLocation alloc] initWithLatitude: min.latitude longitude: min.longitude];\nCLLocation* locSouthEast = [[CLLocation alloc] initWithLatitude: min.latitude longitude: max.longitude];\nCLLocation* locNorthEast = [[CLLocation alloc] initWithLatitude: max.latitude longitude: max.longitude];\nCLLocationCoordinate2D regionCenter;\nregionCenter.latitude = (min.latitude + max.latitude) / 2.0;\nregionCenter.longitude = (min.longitude + max.longitude) / 2.0;\u003Cbr/>\nCLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast];\nCLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest];\nMKCoordinateRegion region;\nregion = MKCoordinateRegionMakeWithDistance( regionCenter, latMeters, lonMeters );\nMKCoordinateRegion fitRegion = [self.mapView regionThatFits: region];\n[self.mapView setRegion: fitRegion animated: YES];\n[locSouthWest release];\n[locSouthEast release];\n[locNorthEast release];\n\n",[101,3737,3738,3742,3747,3751,3756,3761,3766,3771,3776,3781,3786,3791,3796,3801,3806,3811,3816,3820,3825,3830,3835,3840,3845,3850,3855,3860,3865,3870,3875,3880,3885,3890,3895],{"__ignoreMap":26},[104,3739,3740],{"class":106,"line":107},[104,3741,1039],{"emptyLinePlaceholder":36},[104,3743,3744],{"class":106,"line":27},[104,3745,3746],{},"if ( [[self.mapView annotations] count] \u003C 2 )\n",[104,3748,3749],{"class":106,"line":137},[104,3750,3428],{},[104,3752,3753],{"class":106,"line":161},[104,3754,3755],{},"CLLocationCoordinate2D min;\n",[104,3757,3758],{"class":106,"line":174},[104,3759,3760],{},"CLLocationCoordinate2D max;\n",[104,3762,3763],{"class":106,"line":180},[104,3764,3765],{},"BOOL minMaxInitialized = NO;\n",[104,3767,3768],{"class":106,"line":197},[104,3769,3770],{},"for ( id\u003CMKAnnotation> a in [self.mapView annotations] ) {\n",[104,3772,3773],{"class":106,"line":209},[104,3774,3775],{}," if ( !minMaxInitialized ) {\n",[104,3777,3778],{"class":106,"line":226},[104,3779,3780],{}," min = a.coordinate;\n",[104,3782,3783],{"class":106,"line":253},[104,3784,3785],{}," max = a.coordinate;\n",[104,3787,3788],{"class":106,"line":277},[104,3789,3790],{}," minMaxInitialized = YES;\n",[104,3792,3793],{"class":106,"line":288},[104,3794,3795],{}," } else {\n",[104,3797,3798],{"class":106,"line":300},[104,3799,3800],{}," min.latitude = MIN( min.latitude, a.coordinate.latitude );\n",[104,3802,3803],{"class":106,"line":310},[104,3804,3805],{}," min.longitude = MIN( min.longitude, a.coordinate.longitude );\n",[104,3807,3808],{"class":106,"line":1658},[104,3809,3810],{}," max.latitude = MAX( max.latitude, a.coordinate.latitude );\n",[104,3812,3813],{"class":106,"line":1669},[104,3814,3815],{}," max.longitude = MAX( max.longitude, a.coordinate.longitude );\n",[104,3817,3818],{"class":106,"line":1680},[104,3819,3385],{},[104,3821,3822],{"class":106,"line":1695},[104,3823,3824],{},"}\u003Cbr/>\n",[104,3826,3827],{"class":106,"line":1709},[104,3828,3829],{},"CLLocation* locSouthWest = [[CLLocation alloc] initWithLatitude: min.latitude longitude: min.longitude];\n",[104,3831,3832],{"class":106,"line":1722},[104,3833,3834],{},"CLLocation* locSouthEast = [[CLLocation alloc] initWithLatitude: min.latitude longitude: max.longitude];\n",[104,3836,3837],{"class":106,"line":1737},[104,3838,3839],{},"CLLocation* locNorthEast = [[CLLocation alloc] initWithLatitude: max.latitude longitude: max.longitude];\n",[104,3841,3842],{"class":106,"line":1742},[104,3843,3844],{},"CLLocationCoordinate2D regionCenter;\n",[104,3846,3847],{"class":106,"line":1760},[104,3848,3849],{},"regionCenter.latitude = (min.latitude + max.latitude) / 2.0;\n",[104,3851,3852],{"class":106,"line":1779},[104,3853,3854],{},"regionCenter.longitude = (min.longitude + max.longitude) / 2.0;\u003Cbr/>\n",[104,3856,3857],{"class":106,"line":1792},[104,3858,3859],{},"CLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast];\n",[104,3861,3862],{"class":106,"line":1797},[104,3863,3864],{},"CLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest];\n",[104,3866,3867],{"class":106,"line":1818},[104,3868,3869],{},"MKCoordinateRegion region;\n",[104,3871,3872],{"class":106,"line":1834},[104,3873,3874],{},"region = MKCoordinateRegionMakeWithDistance( regionCenter, latMeters, lonMeters );\n",[104,3876,3877],{"class":106,"line":1839},[104,3878,3879],{},"MKCoordinateRegion fitRegion = [self.mapView regionThatFits: region];\n",[104,3881,3882],{"class":106,"line":1854},[104,3883,3884],{},"[self.mapView setRegion: fitRegion animated: YES];\n",[104,3886,3887],{"class":106,"line":1870},[104,3888,3889],{},"[locSouthWest release];\n",[104,3891,3892],{"class":106,"line":1883},[104,3893,3894],{},"[locSouthEast release];\n",[104,3896,3897],{"class":106,"line":1888},[104,3898,3899],{},"[locNorthEast release];\n",[18,3901,3902],{},"The code might look complicated, but we can break it down into 3 steps:",[3904,3905,3906],"ol",{},[1223,3907,3908],{},"Iterate over all our custom annotations (we only have one) to determine the max/min latitude and longitude",[1204,3910,3911,3925],{},[1223,3912,3913,3914,2499,3917,3920,3921,3924],{},"This results in a triangle ",[318,3915,3916],{},"locSouthWest",[318,3918,3919],{},"locSouthEast"," and ",[318,3922,3923],{},"locNorthEast",", that we can use to determine the center\nfor our zoom",[1223,3926,3927,3928,3931],{},"Using the distance between the triangle points and the center, we can fit the map into a ",[318,3929,3930],{},"MKCoordinateRegion",", that\nwill zoom it so that everything fits on the screen.",[18,3933,3934,3935,3938],{},"One thing this algorithm doesn’t include, is the ",[318,3936,3937],{},"AnnotationView"," that display the name and location of the red pin. It\nsometimes happens, that it’s displayed outside of the screen, once you tap on the pin. However, if you add the code\nabove and hook it into the following method:",[95,3940,3942],{"className":875,"code":3941,"language":877,"meta":26,"style":26},"\n- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views\n\n",[101,3943,3944,3948],{"__ignoreMap":26},[104,3945,3946],{"class":106,"line":107},[104,3947,1039],{"emptyLinePlaceholder":36},[104,3949,3950],{"class":106,"line":27},[104,3951,3952],{},"- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views\n",[18,3954,3955,3956,3959],{},"You still need to add the code, which handles the annotation, but you can feel free to steal it\nfrom ",[60,3957,3487],{"href":3703,"rel":3958},[64],". The result should look something\nlike this:",[18,3961,3962],{},[21,3963],{"alt":3712,"src":3964},"https://media.synyx.de/uploads//2010/05/screen_synyx_map.png",[18,3966,3967,3968,3971],{},"This concludes our little tutorial on Google Maps and ",[318,3969,3970],{},"MapKit",". I hope it was helpful! If you need any assistance, just\nleave a comment or drop me an email, I’ll be happy to help you out.",[352,3973,930],{},{"title":26,"searchDepth":27,"depth":27,"links":3975},[],[30,358],"2010-05-19T06:44:08","In\\nmy first installment,\\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\\ndownload the code for this tutorial on github.","https://synyx.de/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii/",{},"/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",{"title":3685,"description":3983},"In\nmy first installment,\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\ndownload the code for this tutorial on github.","blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",[3986,43],"google-maps","In my first installment, we laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can download the code…","Hpc7jZiUS-9fX42oCKNm3kaFp-aPqLQkR6HNXUf1bUU",{"id":3990,"title":3991,"author":3992,"body":3993,"category":4066,"date":4067,"description":4068,"extension":33,"link":4069,"meta":4070,"navigation":36,"path":4071,"seo":4072,"slug":3997,"stem":4074,"tags":4075,"teaser":4076,"__hash__":4077},"blog/blog/lessons-learned-iphone-review.md","Lessons learned – iPhone Review",[9],{"type":11,"value":3994,"toc":4064},[3995,3998,4017,4061],[14,3996,3991],{"id":3997},"lessons-learned-iphone-review",[18,3999,4000,4001,2710,4006,2710,4011,4016],{},"When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple.\nAlthough ",[60,4002,4005],{"href":4003,"rel":4004},"http://apprejections.com/index.php/post/171",[64],"there are",[60,4007,4010],{"href":4008,"rel":4009},"http://www.mobileorchard.com/avoiding-iphone-app-rejection-from-apple/",[64],"plenty of resources",[60,4012,4015],{"href":4013,"rel":4014},"https://web.archive.org/web/20100809102557/http://iphone.derheckser.com:80/2009/07/10/suffering-from-modus-operandi-of-reviewer-team/",[64],"out there",",\nhere’s a short rundown of what we’ve learned ourselves so far:",[1204,4018,4019,4025,4031,4037,4049],{},[1223,4020,4021,4024],{},[580,4022,4023],{},"Marketing Apps are not allowed","\nIf the sole purpose of your App is marketing, you’ll have a hard time getting your App through. You need to add what\nApple calls “user functionality”. That could be something simple like a photo gallery or a feature to reserve a room\nor table.",[1223,4026,4027,4030],{},[580,4028,4029],{},"You cannot tease your users with features that they have to pay for","\nIf you are offering a lite version of your App, you cannot add disabled functionality, which would be enabled in the\npaid version. A lite version usually is offered separately from a paid version, which means the user will constantly\nsee disabled menu items or buttons, since the App will never be updated. Instead add a info section about the paid\nversion in your App, which describes what the paid version offers.",[1223,4032,4033,4036],{},[580,4034,4035],{},"Don’t ask your users to upgrade","\nYou cannot add an alert in a free/lite version of your App, which asks users to upgrade or buy the paid version.\nInstead you should add a “buy me” button or a section in your App further describing what your paid version offers.",[1223,4038,4039,4042,4043,4048],{},[580,4040,4041],{},"Build a working App","\nYou are definitely rejected if your App is buggy! If the reviewer thinks he found a bug, he’ll reject your App. A\ncrash is the worst case scenario,\nbut ",[60,4044,4047],{"href":4045,"rel":4046},"http://dlinsin.blogspot.com/2010/05/don-forget-your-linker-flags.html",[64],"it happens",". However, don’t depend on\nApple as your QA, because the review times are too long to go back and forth this way.",[1223,4050,4051,4054,4055,4060],{},[580,4052,4053],{},"Don’t infringe Trademarks or Copyrights","\nDon’t mention\nApple, ",[60,4056,4059],{"href":4057,"rel":4058},"http://www.geek.com/articles/mobile/apple-demands-a-developer-removes-android-references-from-iphone-app-2010024/",[64],"Android","\nor any other Trademark therefore – as long as you don’t own it. You should also resist to use iPhone like icons or\nimages.",[18,4062,4063],{},"If you respect all of these restrictions and gotchas, you should be save to get your App through the review process. I\nsay “should”, because it all appears to depend on the person who reviews your App. Let us know what you experienced,\nsubmitting your Apps, I bet there are a lot more of these gotchas.",{"title":26,"searchDepth":27,"depth":27,"links":4065},[],[30,358],"2010-05-13T12:37:53","When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple.\\nAlthough there are plenty of resources out there,\\nhere’s a short rundown of what we’ve learned ourselves so far:","https://synyx.de/blog/lessons-learned-iphone-review/",{},"/blog/lessons-learned-iphone-review",{"title":3991,"description":4073},"When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple.\nAlthough there are plenty of resources out there,\nhere’s a short rundown of what we’ve learned ourselves so far:","blog/lessons-learned-iphone-review",[42,43],"When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple. Although there are plenty of resources out there,…","T4mUZEpPLjFmQlCw35HyM72gG1M41S4jYsCCinauV0A",{"id":4079,"title":4080,"author":4081,"body":4082,"category":4284,"date":4285,"description":4286,"extension":33,"link":4287,"meta":4288,"navigation":36,"path":4289,"seo":4290,"slug":4086,"stem":4292,"tags":4293,"teaser":4294,"__hash__":4295},"blog/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i.md","How to add a 'Find Your Company' feature to your iPhone App – Part I",[9],{"type":11,"value":4083,"toc":4282},[4084,4088,4100,4107,4116,4119,4127,4143,4155,4208,4223,4257,4277,4280],[14,4085,4087],{"id":4086},"how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i","How to add a \"Find Your Company\" feature to your iPhone App – Part I",[18,4089,4090,4091,4093,4094,4099],{},"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\nGoogle Maps and the ",[318,4092,3970],{}," framework. I won’t go into the details of MapKit here, since Apple’s documentation is\nawesome and they provide a lot\nof ",[60,4095,4098],{"href":4096,"rel":4097},"http://developer.apple.com/iphone/library/samplecode/CurrentAddress/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009469",[64],"sample code",",\nwhich gets you up and running in no time.",[18,4101,4102,4103,4106],{},"What I’d like to show you today, is some code, which nicely zooms the Map to your office and current location of the App\nuser, once the ",[318,4104,4105],{},"MapView"," is loaded. Here are a couple of screenshots to give you an idea of what I’m talking about:",[18,4108,4109,2710,4111,4115],{},[21,4110],{"alt":3712,"src":3964},[21,4112],{"alt":4113,"src":4114},"\"Synyx Routing\"","https://media.synyx.de/uploads//2010/05/screen_synyx_map_1.png","\nAs you can see, Synyx moved their offices to San Francisco. The iPhone App user, with the blue dot, is currently in\ncupertino. If he taps on the white/blue arrow in the annotation of the red dot, he is asked whether he’d really want to\nleave the App and start Google Maps to route from his current location to the Synyx Offices in San Francisco.",[18,4117,4118],{},"Now, the main focus on this blog post is how to get you using this in your App. I’m gonna split this little tutorial in\n2 parts:",[3904,4120,4121,4124],{},[1223,4122,4123],{},"Showing the Synyx Offices on the Map",[1223,4125,4126],{},"Zooming in, so that everything fits on the screen",[18,4128,4129,4130,4132,4133,4136,4137,4142],{},"If you feel like going off on your own, the code for the ",[318,4131,3731],{}," used, is available for download\non ",[60,4134,3487],{"href":3703,"rel":4135},[64],". The zooming algorithm is borrowed\nfrom ",[60,4138,4141],{"href":4139,"rel":4140},"http://stackoverflow.com/questions/1303265/algorithm-for-determining-minimum-bounding-rectangle-for-collection-of-lat-lon-co/1413264#1413264",[64],"stackoverflow",".\nHowever, the code is one of my first iPhone projects and not polished, so don’t use it blindly. There are a couple of\nissue, e.g. I’m pretty sure it won’t survive a memory warning. It’s meant for demonstration solely!",[18,4144,4145,4146,4148,4149,4151,4152,4154],{},"So let’s get into it. We first need a ",[318,4147,3731],{}," with a ",[318,4150,4105],{}," associated. It is pretty straightforward and\nyou should have no problems doing that in Interface Builder. The next thing you need and that is missing from the code\non github is a custom ",[318,4153,3970],{}," annotation:",[95,4156,4158],{"className":875,"code":4157,"language":877,"meta":26,"style":26},"\n@interface AddressAnnotation : NSObject\u003CMKAnnotation> {\n CLLocationCoordinate2D coordinate;\n NSString *title;\n NSString *subtitle;\n}\n- (id)initWith:(CLLocationCoordinate2D)_coords;\n@property(retain,nonatomic) NSString *title;\n@property(retain,nonatomic) NSString *subtitle;\n@end\n\n",[101,4159,4160,4164,4169,4174,4179,4184,4188,4193,4198,4203],{"__ignoreMap":26},[104,4161,4162],{"class":106,"line":107},[104,4163,1039],{"emptyLinePlaceholder":36},[104,4165,4166],{"class":106,"line":27},[104,4167,4168],{},"@interface AddressAnnotation : NSObject\u003CMKAnnotation> {\n",[104,4170,4171],{"class":106,"line":137},[104,4172,4173],{}," CLLocationCoordinate2D coordinate;\n",[104,4175,4176],{"class":106,"line":161},[104,4177,4178],{}," NSString *title;\n",[104,4180,4181],{"class":106,"line":174},[104,4182,4183],{}," NSString *subtitle;\n",[104,4185,4186],{"class":106,"line":180},[104,4187,313],{},[104,4189,4190],{"class":106,"line":197},[104,4191,4192],{},"- (id)initWith:(CLLocationCoordinate2D)_coords;\n",[104,4194,4195],{"class":106,"line":209},[104,4196,4197],{},"@property(retain,nonatomic) NSString *title;\n",[104,4199,4200],{"class":106,"line":226},[104,4201,4202],{},"@property(retain,nonatomic) NSString *subtitle;\n",[104,4204,4205],{"class":106,"line":253},[104,4206,4207],{},"@end\n",[18,4209,4210,4211,4215,4216,4219,4220,72],{},"This will be responsible for displaying the name and city of the Synyx Offices above the red pin on the map. But it’s\nonly responsible for displaying the information, it doesn’t know where yet. In order to find the location of our offices\nusing the Google Maps API, I came up with a little helper method\ncalled ",[60,4212,4214],{"href":3703,"rel":4213},[64],"synyxLocation (line 175)",". It simply\nreturns the ",[318,4217,4218],{},"CLLocationCoordinate2D"," struct, which is needed in our ",[318,4221,4222],{},"AddressAnnotation",[95,4224,4226],{"className":875,"code":4225,"language":877,"meta":26,"style":26},"\nCLLocationCoordinate2D location = [self synyxLocation];\nself.synyx = [[AddressAnnotation alloc] initWith:location];\n[self.synyx setTitle:@\"Synyx GmbH & Co. KG\"];\n[self.synyx setSubtitle:synyxLoc];\n[mapView addAnnotation:synyx];\n\n",[101,4227,4228,4232,4237,4242,4247,4252],{"__ignoreMap":26},[104,4229,4230],{"class":106,"line":107},[104,4231,1039],{"emptyLinePlaceholder":36},[104,4233,4234],{"class":106,"line":27},[104,4235,4236],{},"CLLocationCoordinate2D location = [self synyxLocation];\n",[104,4238,4239],{"class":106,"line":137},[104,4240,4241],{},"self.synyx = [[AddressAnnotation alloc] initWith:location];\n",[104,4243,4244],{"class":106,"line":161},[104,4245,4246],{},"[self.synyx setTitle:@\"Synyx GmbH & Co. KG\"];\n",[104,4248,4249],{"class":106,"line":174},[104,4250,4251],{},"[self.synyx setSubtitle:synyxLoc];\n",[104,4253,4254],{"class":106,"line":180},[104,4255,4256],{},"[mapView addAnnotation:synyx];\n",[18,4258,4259,4260,4262,4263,4265,4266,4269,4270,4272,4273,4276],{},"Instantiating the ",[318,4261,4222],{}," with the previously determined location and adding it to the ",[318,4264,4105],{}," will to the\nrest. I did this in the ",[318,4267,4268],{},"viewDidLoad"," of the ",[318,4271,3731],{},", which is not be the best place. Maybe the\n",[318,4274,4275],{},"viewWillAppear"," method would have been better.",[18,4278,4279],{},"After adding those parts discussed above, you can fire up the Simulator and get the location of our offices with a red\npin and your current location (in the Simulator it’s always Cupertino). The map doesn’t zoom yet and is not properly\nlocated, we leave that to our next installment.",[352,4281,930],{},{"title":26,"searchDepth":27,"depth":27,"links":4283},[],[30,358],"2010-05-06T09:19:07","We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\\nGoogle Maps and the MapKit framework. I won’t go into the details of MapKit here, since Apple’s documentation is\\nawesome and they provide a lot\\nof sample code,\\nwhich gets you up and running in no time.","https://synyx.de/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i/",{},"/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i",{"title":4080,"description":4291},"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\nGoogle Maps and the MapKit framework. I won’t go into the details of MapKit here, since Apple’s documentation is\nawesome and they provide a lot\nof sample code,\nwhich gets you up and running in no time.","blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i",[3986,43],"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use Google Maps and the MapKit framework.…","xf7CAeTBsMxG4WUKphnA4aL0RIh0fwQNjjYZpLpVrbw",{"id":4297,"title":4298,"author":4299,"body":4300,"category":4335,"date":4336,"description":4337,"extension":33,"link":4338,"meta":4339,"navigation":36,"path":4340,"seo":4341,"slug":4304,"stem":4343,"tags":4344,"teaser":4347,"__hash__":4348},"blog/blog/welcome.md","Welcome",[9],{"type":11,"value":4301,"toc":4333},[4302,4305,4325],[14,4303,4298],{"id":4304},"welcome",[18,4306,4307,4308,2499,4313,4318,4319,4324],{},"This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\nmobile space. No matter if it’s ",[60,4309,4312],{"href":4310,"rel":4311},"http://android.org",[64],"Google’s Android",[60,4314,4317],{"href":4315,"rel":4316},"http://apple.com/iphone",[64],"Apple’s iPhone","\nor ",[60,4320,4323],{"href":4321,"rel":4322},"http://www.meego.com/",[64],"MeeGo"," we have something to say about it!",[18,4326,4327,4328,72],{},"Stay tuned and subscribe to our ",[60,4329,4332],{"href":4330,"rel":4331},"http://mobile.synyx.de/?feed=rss2",[64],"RSS feed",{"title":26,"searchDepth":27,"depth":27,"links":4334},[],[30],"2010-03-26T17:24:38","This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\\nmobile space. No matter if it’s Google’s Android, Apple’s iPhone\\nor MeeGo we have something to say about it!","https://synyx.de/blog/welcome/",{},"/blog/welcome",{"title":4298,"description":4342},"This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\nmobile space. No matter if it’s Google’s Android, Apple’s iPhone\nor MeeGo we have something to say about it!","blog/welcome",[452,43,4345,4346],"meego","synyx","This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the mobile space. No matter if it’s Google’s…","fxYe2l28A3Tr4AN4j9XfZ6v3dxy5234kvArmmK3JoB4",[4350,4353,4356,4359,4362,4365,4368,4371,4374,4377,4380,4383,4386,4389,4392,4395,4398,4401,4404,4407,4410,4413,4415,4418,4421,4424,4427,4429,4432,4435,4438,4441,4444,4447,4450,4453,4456,4459,4462,4465,4468,4471,4474,4477,4480,4483,4486,4489,4492,4495,4498,4501,4504,4507,4510,4513,4516,4518,4521,4524,4527,4530,4533,4535,4538,4541,4544,4547,4549,4552,4555,4558,4561,4564,4566,4569,4572,4575,4578,4581,4584,4587,4590,4593,4596,4599,4602,4605,4608,4611,4614,4617,4620,4623,4626,4629,4632,4635,4638,4640,4643,4646,4649,4652,4654,4657,4660,4663,4666,4669,4672,4675,4678,4681,4684,4687,4690,4693,4696,4699,4702,4705,4708,4711,4714,4717,4720,4723,4726,4729,4730,4733,4736,4739,4742,4745,4748,4751,4754,4757,4760,4763],{"slug":4351,"name":4352},"abel","Jennifer Abel",{"slug":4354,"name":4355},"allmendinger","Otto Allmendinger",{"slug":4357,"name":4358},"antony","Ben Antony",{"slug":4360,"name":4361},"arrasz","Joachim Arrasz",{"slug":4363,"name":4364},"bauer","David Bauer",{"slug":4366,"name":4367},"bechtold","Janine Bechtold",{"slug":4369,"name":4370},"boersig","Jasmin Börsig",{"slug":4372,"name":4373},"buch","Fabian Buch",{"slug":4375,"name":4376},"buchloh","Aljona Buchloh",{"slug":4378,"name":4379},"burgard","Julia Burgard",{"slug":4381,"name":4382},"caspar-schwedes","Caspar Schwedes",{"slug":4384,"name":4385},"christina-schmitt","Christina Schmitt",{"slug":4387,"name":4388},"clausen","Michael Clausen",{"slug":4390,"name":4391},"contargo_poetzsch","Thomas Pötzsch",{"slug":4393,"name":4394},"damrath","Sebastian Damrath",{"slug":4396,"name":4397},"daniel","Markus Daniel",{"slug":4399,"name":4400},"dasch","Julia Dasch",{"slug":4402,"name":4403},"denman","Joffrey Denman",{"slug":4405,"name":4406},"dfuchs","Daniel Fuchs",{"slug":4408,"name":4409},"dobler","Max Dobler",{"slug":4411,"name":4412},"dobriakov","Vladimir Dobriakov",{"slug":4414,"name":4414},"dreiqbik",{"slug":4416,"name":4417},"dschaefer","Denise Schäfer",{"slug":4419,"name":4420},"dschneider","Dominik Schneider",{"slug":4422,"name":4423},"duerlich","Isabell Duerlich",{"slug":4425,"name":4426},"dutkowski","Bernd Dutkowski",{"slug":4428,"name":4428},"eifler",{"slug":4430,"name":4431},"essig","Tim Essig",{"slug":4433,"name":4434},"ferstl","Maximilian Ferstl",{"slug":4436,"name":4437},"fey","Prisca Fey",{"slug":4439,"name":4440},"frank","Leonard Frank",{"slug":4442,"name":4443},"franke","Arnold Franke",{"slug":4445,"name":4446},"frischer","Nicolette Rudmann",{"slug":4448,"name":4449},"fuchs","Petra Fuchs",{"slug":4451,"name":4452},"gari","Sarah Gari",{"slug":4454,"name":4455},"gast","Gast",{"slug":4457,"name":4458},"graf","Johannes Graf",{"slug":4460,"name":4461},"grammlich","Daniela Grammlich",{"slug":4463,"name":4464},"guthardt","Sabrina Guthardt",{"slug":4466,"name":4467},"haeussler","Johannes Häussler",{"slug":4469,"name":4470},"hammann","Daniel Hammann",{"slug":4472,"name":4473},"heetel","Julian Heetel",{"slug":4475,"name":4476},"heft","Florian Heft",{"slug":4478,"name":4479},"heib","Sebastian Heib",{"slug":4481,"name":4482},"heisler","Ida Heisler",{"slug":4484,"name":4485},"helm","Patrick Helm",{"slug":4487,"name":4488},"herbold","Michael Herbold",{"slug":4490,"name":4491},"hofmann","Peter Hofmann",{"slug":4493,"name":4494},"hopf","Florian Hopf",{"slug":4496,"name":4497},"jaud","Alina Jaud",{"slug":4499,"name":4500},"jayasinghe","Robin De Silva Jayasinghe",{"slug":4502,"name":4503},"jbuch","Jonathan Buch",{"slug":4505,"name":4506},"junghanss","Gitta Junghanß",{"slug":4508,"name":4509},"kadyietska","Khrystyna Kadyietska",{"slug":4511,"name":4512},"kannegiesser","Marc Kannegiesser",{"slug":4514,"name":4515},"karoly","Robert Károly",{"slug":949,"name":4517},"Katja Arrasz-Schepanski",{"slug":4519,"name":4520},"kaufmann","Florian Kaufmann",{"slug":4522,"name":4523},"kesler","Mike Kesler",{"slug":4525,"name":4526},"kirchgaessner","Bettina Kirchgäßner",{"slug":4528,"name":4529},"klem","Yannic Klem",{"slug":4531,"name":4532},"klenk","Timo Klenk",{"slug":2798,"name":4534},"Tobias Knell",{"slug":4536,"name":4537},"knoll","Anna-Lena Knoll",{"slug":4539,"name":4540},"knorre","Matthias Knorre",{"slug":4542,"name":4543},"koenig","Melanie König",{"slug":4545,"name":4546},"kraft","Thomas Kraft",{"slug":552,"name":4548},"Florian Krupicka",{"slug":4550,"name":4551},"kuehn","Christian Kühn",{"slug":4553,"name":4554},"lange","Christian Lange",{"slug":4556,"name":4557},"larrasz","Luca Arrasz",{"slug":4559,"name":4560},"leist","Sascha Leist",{"slug":4562,"name":4563},"lihs","Michael Lihs",{"slug":9,"name":4565},"David Linsin",{"slug":4567,"name":4568},"maniyar","Christian Maniyar",{"slug":4570,"name":4571},"martin","Björnie",{"slug":4573,"name":4574},"martin-koch","Martin Koch",{"slug":4576,"name":4577},"matt","Tobias Matt",{"slug":4579,"name":4580},"mennerich","Christian Mennerich",{"slug":4582,"name":4583},"menz","Alexander Menz",{"slug":4585,"name":4586},"meseck","Frederick Meseck",{"slug":4588,"name":4589},"messner","Oliver Messner",{"slug":4591,"name":4592},"michael-ploed","Michael Plöd",{"slug":4594,"name":4595},"mies","Marius Mies",{"slug":4597,"name":4598},"mihai","Alina Mihai",{"slug":4600,"name":4601},"moeller","Jörg Möller",{"slug":4603,"name":4604},"mohr","Rebecca Mohr",{"slug":4606,"name":4607},"moretti","David Moretti",{"slug":4609,"name":4610},"mueller","Sven Müller",{"slug":4612,"name":4613},"muessig","Alexander Müssig",{"slug":4615,"name":4616},"neupokoev","Grigory Neupokoev",{"slug":4618,"name":4619},"nussbaecher","Carmen Nussbächer",{"slug":4621,"name":4622},"ochs","Pascal Ochs",{"slug":4624,"name":4625},"oelhoff","Jan Oelhoff",{"slug":4627,"name":4628},"oengel","Yasin Öngel",{"slug":4630,"name":4631},"oezsoy","Enis Özsoy",{"slug":4633,"name":4634},"posch","Maya Posch",{"slug":4636,"name":4637},"ralfmueller","Ralf Müller",{"slug":4639,"name":4639},"redakteur",{"slug":4641,"name":4642},"reich","Michael Reich",{"slug":4644,"name":4645},"reinhard","Karl-Ludwig Reinhard",{"slug":4647,"name":4648},"rmueller","Rebecca Müller",{"slug":4650,"name":4651},"rosum","Jan Rosum",{"slug":4653,"name":4653},"rueckert",{"slug":4655,"name":4656},"ruessel","Sascha Rüssel",{"slug":4658,"name":4659},"sauter","Moritz Sauter",{"slug":4661,"name":4662},"schaefer","Julian Schäfer",{"slug":4664,"name":4665},"scherer","Petra Scherer",{"slug":4667,"name":4668},"schlicht","Anne Schlicht",{"slug":4670,"name":4671},"schmidt","Jürgen Schmidt",{"slug":4673,"name":4674},"schneider","Tobias Schneider",{"slug":4676,"name":4677},"seber","Benjamin Seber",{"slug":4679,"name":4680},"sommer","Marc Sommer",{"slug":4682,"name":4683},"speaker-fels","Jakob Fels",{"slug":4685,"name":4686},"speaker-gierke","Oliver Gierke",{"slug":4688,"name":4689},"speaker-krupa","Malte Krupa",{"slug":4691,"name":4692},"speaker-mader","Jochen Mader",{"slug":4694,"name":4695},"speaker-meusel","Tim Meusel",{"slug":4697,"name":4698},"speaker-milke","Oliver Milke",{"slug":4700,"name":4701},"speaker-paluch","Mark Paluch",{"slug":4703,"name":4704},"speaker-schad","Jörg Schad",{"slug":4706,"name":4707},"speaker-schalanda","Jochen Schalanda",{"slug":4709,"name":4710},"speaker-schauder","Jens Schauder",{"slug":4712,"name":4713},"speaker-unterstein","Johannes Unterstein",{"slug":4715,"name":4716},"speaker-wolff","Eberhard Wolff",{"slug":4718,"name":4719},"speaker-zoerner","Stefan Zörner",{"slug":4721,"name":4722},"stefan-belger","Stefan Belger",{"slug":4724,"name":4725},"steinegger","Roland Steinegger",{"slug":4727,"name":4728},"stern","sternchen synyx",{"slug":4346,"name":4346},{"slug":4731,"name":4732},"szulc","Mateusz Szulc",{"slug":4734,"name":4735},"tamara","Tamara Tunczinger",{"slug":4737,"name":4738},"theuer","Tobias Theuer",{"slug":4740,"name":4741},"thieme","Sandra Thieme",{"slug":4743,"name":4744},"thies-clasen","Marudor",{"slug":4746,"name":4747},"toernstroem","Olle Törnström",{"slug":4749,"name":4750},"ullinger","Max Ullinger",{"slug":4752,"name":4753},"ulrich","Stephan Ulrich",{"slug":4755,"name":4756},"wagner","Stefan Wagner",{"slug":4758,"name":4759},"weigel","Andreas Weigel",{"slug":4761,"name":4762},"werner","Fabian Werner",{"slug":4764,"name":4765},"wolke","Sören Wolke",["Reactive",4767],{"$scookieConsent":4768,"$ssite-config":4770},{"functional":4769,"analytics":4769},false,{"_priority":4771,"env":4775,"name":4776,"url":4777},{"name":4772,"env":4773,"url":4774},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",4780],{"category-iphone":-1,"authors":-1},"/blog/tags/iphone"]