From f2950a660e0316cc92a8b6bd1688e91940888708 Mon Sep 17 00:00:00 2001 From: Michael Van Milligan Date: Mon, 16 Apr 2018 12:54:57 -0500 Subject: [PATCH 1/4] MOB-833: BVSDKDemo fails to build. --- .../curations/curationsEnduranceCycles.json | 812 ++++++++++++++++++ .../BVSDKDemo.xcodeproj/project.pbxproj | 18 +- Examples/BVSDKDemo/Podfile.lock | 26 +- 3 files changed, 834 insertions(+), 22 deletions(-) create mode 100644 BVSDKTests/MockData/curations/curationsEnduranceCycles.json diff --git a/BVSDKTests/MockData/curations/curationsEnduranceCycles.json b/BVSDKTests/MockData/curations/curationsEnduranceCycles.json new file mode 100644 index 00000000..6b9af081 --- /dev/null +++ b/BVSDKTests/MockData/curations/curationsEnduranceCycles.json @@ -0,0 +1,812 @@ +{ + "status": "ok", + "code": 200, + "results": 13, + "tasks": [], + "updates": [{ + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "Check out my new Endurance. #endurancecyclesdemo", + "id": 119, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://curations-imaging.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 30.3994293, + "longitude": -97.7380764 + }, + "featured_groups": [], + "videos": [], + "channel": "instagram", + "tags": ["6-bv"], + "timestamp": 1430786365, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8qESjwxP/", + "token": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11189193_759854740797962_587375687_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11189193_759854740797962_587375687_n.jpg", + "image_service_url": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xap1%2Ft51.2885-15%2Fe15%2F11189193_759854740797962_587375687_n.jpg&checksum=1c195526", + "id": 119, + "local_url": "https://branddemo.feedmagnet.com/content/photo/119/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8qESjwxP/", + "product_id": null, + "language": "en", + "token": "977829367731129423_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "I'm way faster on my new Endurance. #endurancecyclesdemo", + "id": 118, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/bazaarconsumer", + "username": "bazaarconsumer", + "alias": "Jason Wilson", + "token": "bazaarconsumer", + "avatar": "https://dawn-treader.feedmagnet.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Fbazaarconsumer&checksum=f39fa5d6", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 37.783826, + "longitude": -122.398632 + }, + "featured_groups": [], + "videos": [], + "channel": "facebook", + "tags": ["6-bv"], + "timestamp": 1430786324, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8lBpNNms/", + "token": "https://scontent.cdninstagram.com/hphotos-xpt1/t51.2885-15/e15/11192666_361004090774122_705828060_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xpt1/t51.2885-15/e15/11192666_361004090774122_705828060_n.jpg", + "image_service_url": "https://dawn-treader.feedmagnet.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xpt1%2Ft51.2885-15%2Fe15%2F11192666_361004090774122_705828060_n.jpg&checksum=b05cd1f4", + "id": 118, + "local_url": "https://branddemo.feedmagnet.com/content/photo/118/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8lBpNNms/", + "product_id": null, + "language": "en", + "token": "977829021292485036_1788043381", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "My hands will thank me. New bars. #endurancecyclesdemo", + "id": 116, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://curations-imaging.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 30.5994293, + "longitude": -97.6380764 + }, + "featured_groups": [], + "videos": [], + "channel": "google-plus", + "tags": ["6-bv"], + "timestamp": 1430786290, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8g0AjwxA/", + "token": "https://scontent.cdninstagram.com/hphotos-xpa1/t51.2885-15/e15/928814_437868586390808_1409565515_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xpa1/t51.2885-15/e15/928814_437868586390808_1409565515_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xpa1%2Ft51.2885-15%2Fe15%2F928814_437868586390808_1409565515_n.jpg&checksum=f75f27bc", + "id": 116, + "local_url": "https://branddemo.feedmagnet.com/content/photo/116/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8g0AjwxA/", + "product_id": null, + "language": "en", + "token": "977828731773979712_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "So excited for my first ride on this new Endurance. #endurancecyclesdemo", + "id": 115, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 40.7361079, + "longitude": -73.9934307 + }, + "featured_groups": [], + "videos": [], + "channel": "bazaarvoice", + "tags": ["6-bv"], + "timestamp": 1430786228, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8ZT9jww1/", + "token": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11208567_473476749469908_2091322227_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11208567_473476749469908_2091322227_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xap1%2Ft51.2885-15%2Fe15%2F11208567_473476749469908_2091322227_n.jpg&checksum=dad29392", + "id": 115, + "local_url": "https://branddemo.feedmagnet.com/content/photo/115/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8ZT9jww1/", + "product_id": null, + "language": "en", + "token": "977828216327572533_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "So much new bling in my Endurance. #endurancecyclesdemo", + "id": 112, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/bazaarconsumer", + "username": "bazaarconsumer", + "alias": "Jason Wilson", + "token": "bazaarconsumer", + "avatar": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Fbazaarconsumer&checksum=f39fa5d6", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 40.315894, + "longitude": -74.3449557 + }, + "featured_groups": [], + "videos": [], + "channel": "foursquare", + "tags": ["6-bv"], + "timestamp": 1430786154, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8QOEtNmP/", + "token": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11187111_700801113376522_943714430_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11187111_700801113376522_943714430_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xap1%2Ft51.2885-15%2Fe15%2F11187111_700801113376522_943714430_n.jpg&checksum=b3657da9", + "id": 112, + "local_url": "https://branddemo.feedmagnet.com/content/photo/112/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8QOEtNmP/", + "product_id": null, + "language": "en", + "token": "977827591529748879_1788043381", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "Check my new seat. Endurance for life. #endurancecyclesdemo", + "id": 113, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 41.755803, + "longitude": -87.554512 + }, + "featured_groups": [], + "videos": [], + "channel": "pinterest", + "tags": ["6-bv"], + "timestamp": 1430786117, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8LvzDwwW/", + "token": "https://scontent.cdninstagram.com/hphotos-xaf1/t51.2885-15/e15/11191386_486613334838336_1159301110_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xaf1/t51.2885-15/e15/11191386_486613334838336_1159301110_n.jpg", + "image_service_url": "https://dawn-treader.feedmagnet.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xaf1%2Ft51.2885-15%2Fe15%2F11191386_486613334838336_1159301110_n.jpg&checksum=ae37ae7b", + "id": 113, + "local_url": "https://branddemo.feedmagnet.com/content/photo/113/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8LvzDwwW/", + "product_id": null, + "language": "en", + "token": "977827284143508502_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "Got an awesome new toy. #endurancecyclesdemo", + "id": 114, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/bazaarconsumer", + "username": "bazaarconsumer", + "alias": "Jason Wilson", + "token": "bazaarconsumer", + "avatar": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Fbazaarconsumer&checksum=f39fa5d6", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 41.8882179, + "longitude": -87.6353247 + }, + "featured_groups": [], + "videos": [], + "channel": "rss", + "tags": [], + "timestamp": 1430786083, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8HmTtNmE/", + "token": "https://scontent.cdninstagram.com/hphotos-xfp1/t51.2885-15/e15/11190697_1577702832510275_1186353453_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xfp1/t51.2885-15/e15/11190697_1577702832510275_1186353453_n.jpg", + "image_service_url": "https://curations-imaging.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xfp1%2Ft51.2885-15%2Fe15%2F11190697_1577702832510275_1186353453_n.jpg&checksum=98d68d85", + "id": 114, + "local_url": "https://branddemo.feedmagnet.com/content/photo/114/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8HmTtNmE/", + "product_id": null, + "language": "en", + "token": "977826999075920260_1788043381", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "I'm ready to take my Endurance for a spin. #endurancecyclesdemo", + "id": 109, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 41.863353, + "longitude": -87.617855 + }, + "featured_groups": [], + "videos": [], + "channel": "vimeo", + "tags": ["6-bv"], + "timestamp": 1430786049, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R8DaZDwwD/", + "token": "https://scontent.cdninstagram.com/hphotos-xpa1/t51.2885-15/e15/11190008_819547848132701_1482442527_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xpa1/t51.2885-15/e15/11190008_819547848132701_1482442527_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xpa1%2Ft51.2885-15%2Fe15%2F11190008_819547848132701_1482442527_n.jpg&checksum=1d3f17a7", + "id": 109, + "local_url": "https://branddemo.feedmagnet.com/content/photo/109/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R8DaZDwwD/", + "product_id": null, + "language": "en", + "token": "977826711402908675_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "Blue tape. #endurancecyclesdemo", + "id": 110, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/bazaarconsumer", + "username": "bazaarconsumer", + "alias": "Jason Wilson", + "token": "bazaarconsumer", + "avatar": "https://dawn-treader.feedmagnet.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Fbazaarconsumer&checksum=f39fa5d6", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 41.843662, + "longitude": -87.703686 + }, + "featured_groups": [], + "videos": [], + "channel": "weibo", + "tags": [], + "timestamp": 1430786006, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R7-OfNNl5/", + "token": "https://scontent.cdninstagram.com/hphotos-xpf1/t51.2885-15/e15/11190011_1001000213267298_1992866493_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xpf1/t51.2885-15/e15/11190011_1001000213267298_1992866493_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xpf1%2Ft51.2885-15%2Fe15%2F11190011_1001000213267298_1992866493_n.jpg&checksum=90d0791f", + "id": 110, + "local_url": "https://branddemo.feedmagnet.com/content/photo/110/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R7-OfNNl5/", + "product_id": null, + "language": "fr", + "token": "977826355023763833_1788043381", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "All new crank on this apex 2. #endurancecyclesdemo", + "id": 108, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 37.430007, + "longitude": -122.171745 + }, + "featured_groups": [], + "videos": [], + "channel": "viddy", + "tags": [], + "timestamp": 1430785980, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R76-Ejw_t/", + "token": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11205705_1447972725495519_509915777_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11205705_1447972725495519_509915777_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xap1%2Ft51.2885-15%2Fe15%2F11205705_1447972725495519_509915777_n.jpg&checksum=269ea269", + "id": 108, + "local_url": "https://branddemo.feedmagnet.com/content/photo/108/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R76-Ejw_t/", + "product_id": null, + "language": "en", + "token": "977826131238391789_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "All new bikes. All new attitudes. #endurancecyclesdemo", + "id": 111, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/bazaarconsumer", + "username": "bazaarconsumer", + "alias": "Jason Wilson", + "token": "bazaarconsumer", + "avatar": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Fbazaarconsumer&checksum=f39fa5d6", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": null, + "longitude": null + }, + "featured_groups": [], + "videos": [], + "channel": "youtube", + "tags": [], + "timestamp": 1430785943, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R72fItNlt/", + "token": "https://scontent.cdninstagram.com/hphotos-xft1/t51.2885-15/e15/11192759_806980619379444_844504477_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xft1/t51.2885-15/e15/11192759_806980619379444_844504477_n.jpg", + "image_service_url": "https://curations-imaging.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xft1%2Ft51.2885-15%2Fe15%2F11192759_806980619379444_844504477_n.jpg&checksum=65c43c70", + "id": 111, + "local_url": "https://branddemo.feedmagnet.com/content/photo/111/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R72fItNlt/", + "product_id": null, + "language": "en", + "token": "977825823144073581_1788043381", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "Love the new handles and brakes #endurancecyclesdemo", + "id": 107, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/fastcycle1", + "username": "fastcycle1", + "alias": "Greg Rivers", + "token": "fastcycle1", + "avatar": "https://dawn-treader.feedmagnet.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Ffastcycle1&checksum=0be4b717", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 30.3359176, + "longitude": -97.9863477 + }, + "featured_groups": [], + "videos": [], + "channel": "twitter", + "tags": [], + "timestamp": 1430785915, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R7zGNjw_Z/", + "token": "https://scontent.cdninstagram.com/hphotos-xpa1/t51.2885-15/e15/11205910_745860328864673_1787902349_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xpa1/t51.2885-15/e15/11205910_745860328864673_1787902349_n.jpg", + "image_service_url": "https://curations-imaging-b.nexus.bazaarvoice.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xpa1%2Ft51.2885-15%2Fe15%2F11205910_745860328864673_1787902349_n.jpg&checksum=3328625e", + "id": 107, + "local_url": "https://branddemo.feedmagnet.com/content/photo/107/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R7zGNjw_Z/", + "product_id": null, + "language": "en", + "token": "977825590223507417_1974460962", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }, { + "html": null, + "data": { + "rating": null, + "classification": "photo", + "text": "I'm ready. So is my bike. #endurancecyclesdemo", + "id": 106, + "praises": 0, + "explicit_permission_status": "uninitiated", + "author": { + "profile": "http://instagram.com/bazaarconsumer", + "username": "bazaarconsumer", + "alias": "Jason Wilson", + "token": "bazaarconsumer", + "avatar": "https://curations-imaging-c.nexus.bazaarvoice.com/?url=https%3A%2F%2Favatars.io%2Finstagram%2Fbazaarconsumer&checksum=f39fa5d6", + "channel": "instagram" + }, + "links": [], + "coordinates": { + "latitude": 30.0782106, + "longitude": -97.8447957 + }, + "featured_groups": [], + "videos": [], + "channel": "instagram", + "tags": [], + "timestamp": 1430785858, + "photos": [{ + "origin": "instagram", + "permalink": "https://instagram.com/p/2R7sFWNNlc/", + "token": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11191020_1437098266585326_656993019_n.jpg", + "role": "photo", + "display_url": null, + "url": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/e15/11191020_1437098266585326_656993019_n.jpg", + "image_service_url": "https://dawn-treader.feedmagnet.com/?url=https%3A%2F%2Fscontent.cdninstagram.com%2Fhphotos-xap1%2Ft51.2885-15%2Fe15%2F11191020_1437098266585326_656993019_n.jpg&checksum=409c2107", + "id": 106, + "local_url": "https://branddemo.feedmagnet.com/content/photo/106/" + }], + "teaser": "", + "groups": ["endurancecyclesdemo"], + "permalink": "https://instagram.com/p/2R7sFWNNlc/", + "product_id": null, + "language": "en", + "token": "977825108258511196_1788043381", + "place": null, + "reply_to": null, + "sourceClient": "branddemo" + } + }], + "options": { + "limit": 13, + "client": "branddemo", + "passkey": "kuuqd395w5u7gv43987gxshh", + "identifier": "FeedSource", + "actual_limit": "13", + "withProductData": "true", + "groups": "endurancecyclesdemo" + }, + "productData": { + "6-bv": { + "Brand": { + "Name": "Endurance", + "Id": "Endurance" + }, + "UPCs": ["100000000052"], + "ISBNs": [], + "Description": null, + "ManufacturerPartNumbers": [], + "Attributes": {}, + "QuestionIds": [], + "BrandExternalId": "Endurance", + "ProductPageUrl": "http://www.endurancecycles.com/products/endurance-apex-2", + "FamilyIds": [], + "AttributesOrder": [], + "ImageUrl": "http://cdn.shopify.com/s/files/1/0796/3917/files/Endurance_Apex_2_THUMB.png?5905770545529023052", + "EANs": [], + "Name": "Endurance Apex 2", + "CategoryId": "WomensBikes", + "TotalReviewCount": 40, + "ReviewIds": [], + "Id": "6-bv", + "StoryIds": [], + "ReviewStatistics": { + "ContextDataDistributionOrder": ["Age", "Gender", "PrimaryUse", "Experience", "Brand", "Where"], + "HelpfulVoteCount": 13, + "NotRecommendedCount": 2, + "AverageOverallRating": 4.3, + "SecondaryRatingsAveragesOrder": ["Value", "Comfort", "Quality"], + "FeaturedReviewCount": 0, + "TagDistributionOrder": [], + "NotHelpfulVoteCount": 0, + "OverallRatingRange": 5, + "SecondaryRatingsAverages": { + "Value": { + "Id": "Value", + "AverageRating": 4.275 + }, + "Quality": { + "Id": "Quality", + "AverageRating": 3.975 + }, + "Comfort": { + "Id": "Comfort", + "AverageRating": 3.775 + } + }, + "RatingDistribution": [{ + "Count": 4, + "RatingValue": 1 + }, { + "Count": 1, + "RatingValue": 2 + }, { + "Count": 2, + "RatingValue": 3 + }, { + "Count": 5, + "RatingValue": 4 + }, { + "Count": 28, + "RatingValue": 5 + }], + "TotalReviewCount": 40, + "RatingsOnlyReviewCount": 0, + "RecommendedCount": 38, + "TagDistribution": {}, + "ContextDataDistribution": { + "Where": { + "Values": [{ + "Value": "Endurance-website", + "Count": 9 + }, { + "Value": "Performance-Bicycles", + "Count": 8 + }, { + "Value": "Rugged-Outfitters", + "Count": 9 + }, { + "Value": "Other", + "Count": 14 + }], + "Label": "Where", + "Id": "Where" + }, + "Brand": { + "Values": [{ + "Value": "This-is-my-first-bike", + "Count": 9 + }, { + "Value": "Trek", + "Count": 11 + }, { + "Value": "Specialized", + "Count": 5 + }, { + "Value": "Diamondback", + "Count": 2 + }, { + "Value": "Schwinn", + "Count": 1 + }, { + "Value": "Endurance", + "Count": 7 + }, { + "Value": "Other", + "Count": 5 + }], + "Label": "Brand", + "Id": "Brand" + }, + "Experience": { + "Values": [{ + "Value": "Beginner", + "Count": 15 + }, { + "Value": "Intermediate", + "Count": 20 + }, { + "Value": "Expert", + "Count": 5 + }], + "Label": "Experience", + "Id": "Experience" + }, + "PrimaryUse": { + "Values": [{ + "Value": "Commuting", + "Count": 1 + }, { + "Value": "Racing", + "Count": 19 + }, { + "Value": "Leisure", + "Count": 3 + }, { + "Value": "Exercise", + "Count": 17 + }], + "Label": "Primary Use", + "Id": "PrimaryUse" + }, + "Age": { + "Values": [{ + "Value": "25to34", + "Count": 23 + }, { + "Value": "35to44", + "Count": 13 + }, { + "Value": "45to54", + "Count": 4 + }], + "Label": "Age", + "Id": "Age" + }, + "Gender": { + "Values": [{ + "Value": "Female", + "Count": 40 + }], + "Label": "Gender", + "Id": "Gender" + } + }, + "FirstSubmissionTime": "2016-04-02T04:31:12.000+00:00", + "LastSubmissionTime": "2016-04-30T06:20:39.000+00:00" + }, + "ModelNumbers": [] + } + } +} diff --git a/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj b/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj index 7f42aebd..cf5e84be 100644 --- a/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj +++ b/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj @@ -321,10 +321,10 @@ 8763B3181D1DC65B00C71F54 /* enduranceCyclesSanFrancisco.gpx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = enduranceCyclesSanFrancisco.gpx; path = gpx/enduranceCyclesSanFrancisco.gpx; sourceTree = ""; }; 876FF1B51D21DDFF000ED410 /* FacebookLoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FacebookLoginViewController.xib; path = BVSDKDemo/FacebookLoginViewController.xib; sourceTree = ""; }; 876FF1B71D22C0D7000ED410 /* NotificationPermissionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NotificationPermissionViewController.swift; path = BVSDKDemo/NotificationPermissionViewController.swift; sourceTree = ""; }; - 8772167A1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_FilterLocation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_FilterLocation.json; path = ../../../BVSDKTests/MockData/conversations/sortingReviews/conversationsReviewsEnduranceCycles_FilterLocation.json; sourceTree = ""; }; - 8772167B1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_SortHighestRated.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_SortHighestRated.json; path = ../../../BVSDKTests/MockData/conversations/sortingReviews/conversationsReviewsEnduranceCycles_SortHighestRated.json; sourceTree = ""; }; - 8772167C1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_SortLowestRated.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_SortLowestRated.json; path = ../../../BVSDKTests/MockData/conversations/sortingReviews/conversationsReviewsEnduranceCycles_SortLowestRated.json; sourceTree = ""; }; - 8772167D1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_SortMostHelpful.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_SortMostHelpful.json; path = ../../../BVSDKTests/MockData/conversations/sortingReviews/conversationsReviewsEnduranceCycles_SortMostHelpful.json; sourceTree = ""; }; + 8772167A1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_FilterLocation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_FilterLocation.json; path = ../../../BVSDKTests/MockData/conversations/Display/sortingReviews/conversationsReviewsEnduranceCycles_FilterLocation.json; sourceTree = ""; }; + 8772167B1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_SortHighestRated.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_SortHighestRated.json; path = ../../../BVSDKTests/MockData/conversations/Display/sortingReviews/conversationsReviewsEnduranceCycles_SortHighestRated.json; sourceTree = ""; }; + 8772167C1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_SortLowestRated.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_SortLowestRated.json; path = ../../../BVSDKTests/MockData/conversations/Display/sortingReviews/conversationsReviewsEnduranceCycles_SortLowestRated.json; sourceTree = ""; }; + 8772167D1D3FDAD4007E5C6C /* conversationsReviewsEnduranceCycles_SortMostHelpful.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles_SortMostHelpful.json; path = ../../../BVSDKTests/MockData/conversations/Display/sortingReviews/conversationsReviewsEnduranceCycles_SortMostHelpful.json; sourceTree = ""; }; 877AD26F1EDF4C1C006C7070 /* ReviewCommentsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReviewCommentsViewController.swift; path = Conversations/ReviewCommentsViewController.swift; sourceTree = ""; }; 877AD2701EDF4C1C006C7070 /* ReviewCommentsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ReviewCommentsViewController.xib; path = Conversations/ReviewCommentsViewController.xib; sourceTree = ""; }; 877AD27A1EDF7051006C7070 /* ReviewCommentTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReviewCommentTableViewCell.swift; sourceTree = ""; }; @@ -336,13 +336,13 @@ 878309941CEDF4070097FC48 /* AskAQuestionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AskAQuestionViewController.swift; path = Conversations/AskAQuestionViewController.swift; sourceTree = ""; }; 878309951CEDF4070097FC48 /* AskAQuestionViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = AskAQuestionViewController.xib; path = Conversations/AskAQuestionViewController.xib; sourceTree = ""; }; 879144991CF8BAAE00976220 /* revolution_cycles.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = revolution_cycles.png; sourceTree = ""; }; - 8792AB971D91E6140055B519 /* storeBulkFeedWithStatistics.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = storeBulkFeedWithStatistics.json; path = ../../../BVSDKTests/MockData/conversations/stores/storeBulkFeedWithStatistics.json; sourceTree = ""; }; - 87A02B551E018AC30002701B /* testNotificationProductConfig.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = testNotificationProductConfig.json; path = ../../../BVSDKTests/MockData/conversations/testNotificationProductConfig.json; sourceTree = ""; }; - 87A3192A1CF4938E000D3D0F /* conversationsQuestionsIncludeAnswers.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsQuestionsIncludeAnswers.json; path = ../../../BVSDKTests/MockData/conversations/conversationsQuestionsIncludeAnswers.json; sourceTree = ""; }; + 8792AB971D91E6140055B519 /* storeBulkFeedWithStatistics.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = storeBulkFeedWithStatistics.json; path = ../../../BVSDKTests/MockData/conversations/Display/stores/storeBulkFeedWithStatistics.json; sourceTree = ""; }; + 87A02B551E018AC30002701B /* testNotificationProductConfig.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = testNotificationProductConfig.json; path = ../../../BVSDKTests/MockData/conversations/Display/testNotificationProductConfig.json; sourceTree = ""; }; + 87A3192A1CF4938E000D3D0F /* conversationsQuestionsIncludeAnswers.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsQuestionsIncludeAnswers.json; path = ../../../BVSDKTests/MockData/conversations/Display/conversationsQuestionsIncludeAnswers.json; sourceTree = ""; }; 87A3192E1CF493AF000D3D0F /* recommendationsResult.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = recommendationsResult.json; path = ../../../BVSDKTests/MockData/recommendations/recommendationsResult.json; sourceTree = ""; }; 87A319301CF493D5000D3D0F /* userProfile1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = userProfile1.json; path = ../../../BVSDKTests/MockData/recommendations/userProfile1.json; sourceTree = ""; }; 87A319321CF493E3000D3D0F /* curationsEnduranceCycles.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = curationsEnduranceCycles.json; path = ../../../BVSDKTests/MockData/curations/curationsEnduranceCycles.json; sourceTree = ""; }; - 87A319341CF4942F000D3D0F /* conversationsReviewsEnduranceCycles.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles.json; path = ../../../BVSDKTests/MockData/conversations/conversationsReviewsEnduranceCycles.json; sourceTree = ""; }; + 87A319341CF4942F000D3D0F /* conversationsReviewsEnduranceCycles.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = conversationsReviewsEnduranceCycles.json; path = ../../../BVSDKTests/MockData/conversations/Display/conversationsReviewsEnduranceCycles.json; sourceTree = ""; }; 87B97E941DEF218C005B8C85 /* CartViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CartViewController.swift; path = Cart/CartViewController.swift; sourceTree = ""; }; 87B97E951DEF218C005B8C85 /* CartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CartViewController.xib; path = Cart/CartViewController.xib; sourceTree = ""; }; 87B97EB81DEF8CE4005B8C85 /* UIBarButtonItem+Badge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Badge.swift"; sourceTree = ""; }; @@ -365,7 +365,7 @@ 87CF9A691DEE3D0A0069D2B9 /* CartProductTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CartProductTableViewCell.xib; path = Cart/CartProductTableViewCell.xib; sourceTree = ""; }; 87F24D821DAE8147002231D6 /* enduranceCyclesChicago.gpx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = enduranceCyclesChicago.gpx; path = gpx/enduranceCyclesChicago.gpx; sourceTree = ""; }; 87F24D831DAE8147002231D6 /* enduranceCyclesNY.gpx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = enduranceCyclesNY.gpx; path = gpx/enduranceCyclesNY.gpx; sourceTree = ""; }; - 87F5B9351DC38F37004D2297 /* testNotificationConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = testNotificationConfig.json; path = ../../../BVSDKTests/MockData/conversations/testNotificationConfig.json; sourceTree = ""; }; + 87F5B9351DC38F37004D2297 /* testNotificationConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = testNotificationConfig.json; path = ../../../BVSDKTests/MockData/conversations/Display/testNotificationConfig.json; sourceTree = ""; }; BC927E763E958B0ADE062DDB /* Pods-Curations Custom Post Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Curations Custom Post Extension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Curations Custom Post Extension/Pods-Curations Custom Post Extension.debug.xcconfig"; sourceTree = ""; }; BF76865CF343C4C4833D6B9B /* Pods_Curations_Custom_Post_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Curations_Custom_Post_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ diff --git a/Examples/BVSDKDemo/Podfile.lock b/Examples/BVSDKDemo/Podfile.lock index ebe216cf..fff2af29 100644 --- a/Examples/BVSDKDemo/Podfile.lock +++ b/Examples/BVSDKDemo/Podfile.lock @@ -5,25 +5,25 @@ PODS: - Bolts/AppLinks (1.8.4): - Bolts/Tasks - Bolts/Tasks (1.8.4) - - BVSDK (6.9.0): - - BVSDK/BVCommon (= 6.9.0) - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVCommonUI (6.9.0) - - BVSDK/BVConversations (6.9.0): + - BVSDK (7.0.0): + - BVSDK/BVCommon (= 7.0.0) + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVCommonUI (7.0.0) + - BVSDK/BVConversations (7.0.0): - BVSDK/BVCommon - - BVSDK/BVConversationsStores (6.9.0): + - BVSDK/BVConversationsStores (7.0.0): - BVSDK/BVConversations - - BVSDK/BVConversationsUI (6.9.0): + - BVSDK/BVConversationsUI (7.0.0): - BVSDK/BVCommonUI - BVSDK/BVConversationsStores - - BVSDK/BVCurations (6.9.0): + - BVSDK/BVCurations (7.0.0): - BVSDK/BVCommon - - BVSDK/BVCurationsUI (6.9.0): + - BVSDK/BVCurationsUI (7.0.0): - BVSDK/BVCommonUI - BVSDK/BVCurations - - BVSDK/BVNotifications (6.9.0): + - BVSDK/BVNotifications (7.0.0): - BVSDK/BVConversationsUI - - BVSDK/BVRecommendations (6.9.0): + - BVSDK/BVRecommendations (7.0.0): - BVSDK/BVCommon - Crashlytics (3.9.0): - Fabric (~> 1.7.0) @@ -105,7 +105,7 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: Bolts: 8a7995239dbe724f9cba2248b766d48b7ebdd322 - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a Crashlytics: 64aad5dd97249dd3ff94b979fea140144590cdd3 Fabric: e6be012366472553807dada21243c5ab8d904151 FBSDKCoreKit: ebc6c5f5b3f0e72396041050e2266781a38ac587 @@ -124,4 +124,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: f2145d845c38000175311cab40ce05f8499461d6 -COCOAPODS: 1.3.1 +COCOAPODS: 1.4.0 From 4b5ef6db5a5b4e5a3f785be95bbf524bc4cc068d Mon Sep 17 00:00:00 2001 From: Michael Van Milligan Date: Mon, 18 Jun 2018 10:00:29 -0500 Subject: [PATCH 2/4] Update for Xcode 9.4. --- BVSDK.xcodeproj/project.pbxproj | 101 +- .../xcschemes/BVSDK-Tests.xcscheme | 4 +- .../xcschemes/BVSDK-Universal.xcscheme | 4 +- .../xcshareddata/xcschemes/BVSDK.xcscheme | 4 +- .../BVAnalytics/Private/BVAnalyticsManager.m | 880 +++++++++--------- BVSDK/BVCommon/BVSDKManager.m | 406 ++++---- BVSDK/BVCommon/Private/BVNetworkingManager.m | 4 +- .../Submission/Answer/BVAnswerSubmission.m | 494 +++++----- .../BVCurationsPostViewController.m | 265 +++--- .../BVCurationsUICollectionView.m | 555 +++++------ ...VProductReviewNotificationViewController.m | 206 ++-- ...uctReviewNotificationConfigurationLoader.m | 2 +- .../BVStoreNotificationConfigurationLoader.m | 2 +- BVSDKTests/CommonTests/BVBaseStubTestCase.m | 208 +++-- .../BVNetworkDelegateTestsDelegate.m | 8 +- .../SubmissionTests/UASSubmissionTests.swift | 1 - .../BVSDKDemo.xcodeproj/project.pbxproj | 38 +- .../xcshareddata/xcschemes/BVSDKDemo.xcscheme | 4 +- .../BVSDKDemo/CallToActionCell.swift | 6 +- .../BVSDKDemo/Curations/JPSThumbnail.h | 2 +- .../Curations/JPSThumbnailAnnotation.m | 67 +- .../Frameworks/SweetAlert.swift | 22 +- .../Product Recommendations/Util.swift | 4 +- .../ProductPageButtonCell.swift | 6 +- .../RatingTableViewCell.swift | 2 +- Examples/BVSDKDemo/Podfile.lock | 22 +- .../project.pbxproj | 34 +- .../AuthorViewController.m | 2 +- Examples/Conversations/Obj-C/Podfile.lock | 16 +- .../project.pbxproj | 39 +- Examples/Conversations/Swift/Podfile.lock | 16 +- .../project.pbxproj | 34 +- Examples/Curations/Obj-C/Podfile.lock | 18 +- .../project.pbxproj | 45 +- .../Base.lproj/LaunchScreen.storyboard | 16 +- .../CurationsExample/ViewController.swift | 2 +- Examples/Curations/Swift/Podfile.lock | 26 +- .../ProductRecommendations/Obj-C/Podfile.lock | 15 +- .../project.pbxproj | 34 +- .../ProductRecommendations/Swift/Podfile.lock | 15 +- .../project.pbxproj | 39 +- .../RecommendationsExample/DemoCell.swift | 46 +- Podfile.lock | 6 +- 43 files changed, 1842 insertions(+), 1878 deletions(-) diff --git a/BVSDK.xcodeproj/project.pbxproj b/BVSDK.xcodeproj/project.pbxproj index 95da377f..2e0e73d1 100644 --- a/BVSDK.xcodeproj/project.pbxproj +++ b/BVSDK.xcodeproj/project.pbxproj @@ -18,17 +18,6 @@ name = "BVSDK-Universal"; productName = "BVSDK-Universal"; }; - B561DE1C1FC3838F0019BE89 /* CommonCryptoModuleMap */ = { - isa = PBXAggregateTarget; - buildConfigurationList = B561DE1D1FC3838F0019BE89 /* Build configuration list for PBXAggregateTarget "CommonCryptoModuleMap" */; - buildPhases = ( - B561DE201FC383D20019BE89 /* ShellScript */, - ); - dependencies = ( - ); - name = CommonCryptoModuleMap; - productName = CommonCryptoModuleMap; - }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ @@ -593,13 +582,6 @@ remoteGlobalIDString = 87F2DAA61DAD579D00FB43F3; remoteInfo = BVSDK; }; - B561DE211FC384340019BE89 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 87F2DA9E1DAD579D00FB43F3 /* Project object */; - proxyType = 1; - remoteGlobalIDString = B561DE1C1FC3838F0019BE89; - remoteInfo = CommonCryptoModuleMap; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -2633,12 +2615,10 @@ 87F2DAAD1DAD579D00FB43F3 /* Frameworks */, 87F2DAAE1DAD579D00FB43F3 /* Resources */, 8FD522D26F8D16CB58E49BBE /* [CP] Embed Pods Frameworks */, - 2B613BCF4BFD2CDDE34996AF /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( - B561DE221FC384340019BE89 /* PBXTargetDependency */, 87F2DAB31DAD579D00FB43F3 /* PBXTargetDependency */, ); name = BVSDKTests; @@ -2652,7 +2632,7 @@ 87F2DA9E1DAD579D00FB43F3 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 87F2DAA61DAD579D00FB43F3 = { @@ -2670,10 +2650,6 @@ CreatedOnToolsVersion = 8.0; ProvisioningStyle = Automatic; }; - B561DE1C1FC3838F0019BE89 = { - CreatedOnToolsVersion = 9.1; - ProvisioningStyle = Automatic; - }; }; }; buildConfigurationList = 87F2DAA11DAD579D00FB43F3 /* Build configuration list for PBXProject "BVSDK" */; @@ -2691,7 +2667,6 @@ 87F2DAA61DAD579D00FB43F3 /* BVSDK */, 87F2DAAF1DAD579D00FB43F3 /* BVSDKTests */, 87F2DDC71DAD6C6E00FB43F3 /* BVSDK-Universal */, - B561DE1C1FC3838F0019BE89 /* CommonCryptoModuleMap */, ); }; /* End PBXProject section */ @@ -2791,21 +2766,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 2B613BCF4BFD2CDDE34996AF /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BVSDKTests/Pods-BVSDKTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 32EDD6FE42A25B4578CF0D6C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2855,19 +2815,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BVSDKTests/Pods-BVSDKTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - B561DE201FC383D20019BE89 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "# This if-statement means we'll only run the main script if the CommonCryptoModuleMap directory doesn't exist\n# Because otherwise the rest of the script causes a full recompile for anything where CommonCrypto is a dependency\n# Do a \"Clean Build Folder\" to remove this directory and trigger the rest of the script to run\nif [ -d \"${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap\" ]; then\necho \"${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap directory already exists, so skipping the rest of the script.\"\nexit 0\nfi\n\nmkdir -p \"${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap\"\ncat < \"${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap/module.modulemap\"\nmodule CommonCrypto [system] {\n header \"${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h\"\n export *\n}\nEOF"; - }; B561DE251FCC82900019BE89 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -3132,11 +3079,6 @@ target = 87F2DAA61DAD579D00FB43F3 /* BVSDK */; targetProxy = 87F2DAB21DAD579D00FB43F3 /* PBXContainerItemProxy */; }; - B561DE221FC384340019BE89 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = B561DE1C1FC3838F0019BE89 /* CommonCryptoModuleMap */; - targetProxy = B561DE211FC384340019BE89 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -3153,6 +3095,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -3160,6 +3103,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -3192,7 +3136,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -3212,6 +3156,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -3219,6 +3164,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -3245,7 +3191,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -3276,7 +3222,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.2; }; name = Debug; }; @@ -3302,7 +3247,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.bvsdk; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.2; }; name = Release; }; @@ -3317,7 +3261,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "./BVSDKTests/Support/BVSDKTests-Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -3332,7 +3275,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "./BVSDKTests/Support/BVSDKTests-Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 3.0; }; name = Release; }; @@ -3340,7 +3282,6 @@ isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.2; }; name = Debug; }; @@ -3348,25 +3289,6 @@ isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.2; - }; - name = Release; - }; - B561DE1E1FC3838F0019BE89 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.2; - }; - name = Debug; - }; - B561DE1F1FC3838F0019BE89 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.2; }; name = Release; }; @@ -3409,15 +3331,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - B561DE1D1FC3838F0019BE89 /* Build configuration list for PBXAggregateTarget "CommonCryptoModuleMap" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - B561DE1E1FC3838F0019BE89 /* Debug */, - B561DE1F1FC3838F0019BE89 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 87F2DA9E1DAD579D00FB43F3 /* Project object */; diff --git a/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK-Tests.xcscheme b/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK-Tests.xcscheme index 7ff09cf0..e8d6377d 100644 --- a/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK-Tests.xcscheme +++ b/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK-Tests.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK.xcscheme b/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK.xcscheme index 510e2346..dede907f 100644 --- a/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK.xcscheme +++ b/BVSDK.xcodeproj/xcshareddata/xcschemes/BVSDK.xcscheme @@ -1,6 +1,6 @@ localeUpdateNotificationCenterToken; +@property (nonatomic, strong) id localeUpdateNotificationCenterToken; /// Testing -@property(nonatomic, strong, readonly) +@property (nonatomic, strong, readonly) NSMutableDictionary *testImpressionEventCompletionQueue; -@property(nonatomic, strong, readonly) +@property (nonatomic, strong, readonly) NSMutableDictionary *testPageViewEventCompletionQueue; @@ -59,626 +59,630 @@ @implementation BVAnalyticsManager static BVAnalyticsManager *analyticsInstance = nil; + (BVAnalyticsManager *)sharedManager { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - analyticsInstance = [[self alloc] init]; - }); + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + analyticsInstance = [[self alloc] init]; + }); - return analyticsInstance; + return analyticsInstance; } #pragma mark - Class Properties - (void)setFlushInterval:(NSTimeInterval)newFlushInterval { - dispatch_sync(self.timerEventQueue, ^{ - self.queueFlushInterval = newFlushInterval; - }); + dispatch_sync(self.timerEventQueue, ^{ + self.queueFlushInterval = newFlushInterval; + }); } - (NSMutableDictionary *) testImpressionEventCompletionQueue { + if (!_testImpressionEventCompletionQueue) { + _testImpressionEventCompletionQueue = [NSMutableDictionary dictionary]; + } - if (!_testImpressionEventCompletionQueue) { - _testImpressionEventCompletionQueue = [NSMutableDictionary dictionary]; - } - - return _testImpressionEventCompletionQueue; + return _testImpressionEventCompletionQueue; } - (NSMutableDictionary *) testPageViewEventCompletionQueue { + if (!_testPageViewEventCompletionQueue) { + _testPageViewEventCompletionQueue = [NSMutableDictionary dictionary]; + } - if (!_testPageViewEventCompletionQueue) { - _testPageViewEventCompletionQueue = [NSMutableDictionary dictionary]; - } - - return _testPageViewEventCompletionQueue; + return _testPageViewEventCompletionQueue; } #pragma mark - Class Init - (id)init { - if ((self = [super init])) { - self.eventQueue = [NSMutableArray array]; - self.pageviewQueue = [NSMutableArray array]; - self.concurrentEventQueue = dispatch_queue_create( - "com.bazaarvoice.analyticEventQueue", DISPATCH_QUEUE_CONCURRENT); - self.timerEventQueue = dispatch_queue_create( - "com.bazaarvoice.timerEventQueue", DISPATCH_QUEUE_SERIAL); - self.localeUpdateNotificationTokenQueue = dispatch_queue_create( - "com.bazaarvoice.notificationTokenQueue", DISPATCH_QUEUE_SERIAL); - - [self setFlushInterval:10.0]; - [self registerForAppStateChanges]; - } - return self; + if ((self = [super init])) { + self.eventQueue = [NSMutableArray array]; + self.pageviewQueue = [NSMutableArray array]; + self.concurrentEventQueue = dispatch_queue_create( + "com.bazaarvoice.analyticEventQueue", DISPATCH_QUEUE_CONCURRENT); + self.timerEventQueue = dispatch_queue_create( + "com.bazaarvoice.timerEventQueue", DISPATCH_QUEUE_SERIAL); + self.localeUpdateNotificationTokenQueue = dispatch_queue_create( + "com.bazaarvoice.notificationTokenQueue", DISPATCH_QUEUE_SERIAL); + + [self setFlushInterval:10.0]; + [self registerForAppStateChanges]; + } + return self; } #pragma mark - Analytics Constant Parameters - (NSMutableDictionary *)getMobileDiagnosticParams { - // get diagnostic data - NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; - - NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - NSString *osVersion = [[UIDevice currentDevice] systemVersion]; - NSString *majorMinor = - [infoDictionary objectForKey:@"CFBundleShortVersionString"]; - NSString *build = - [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey]; - NSString *appVersion = - [NSString stringWithFormat:@"%@.%@", majorMinor, build]; - - struct utsname systemInfo; - uname(&systemInfo); - NSString *platform = [NSString stringWithCString:systemInfo.machine - encoding:NSUTF8StringEncoding]; - - NSMutableDictionary *params = [NSMutableDictionary dictionary]; - [params setValue:bundleIdentifier forKey:@"mobileAppIdentifier"]; - [params setValue:appVersion forKey:@"mobileAppVersion"]; - [params setValue:osVersion forKey:@"mobileOSVersion"]; - [params setValue:@"ios" forKey:@"mobileOS"]; - [params setValue:platform forKey:@"mobileDeviceName"]; - [params setValue:BV_SDK_VERSION forKey:@"bvSDKVersion"]; - - return params; + // get diagnostic data + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + NSString *osVersion = [[UIDevice currentDevice] systemVersion]; + NSString *majorMinor = + [infoDictionary objectForKey:@"CFBundleShortVersionString"]; + NSString *build = + [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey]; + NSString *appVersion = + [NSString stringWithFormat:@"%@.%@", majorMinor, build]; + + struct utsname systemInfo; + uname(&systemInfo); + NSString *platform = [NSString stringWithCString:systemInfo.machine + encoding:NSUTF8StringEncoding]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:bundleIdentifier forKey:@"mobileAppIdentifier"]; + [params setValue:appVersion forKey:@"mobileAppVersion"]; + [params setValue:osVersion forKey:@"mobileOSVersion"]; + [params setValue:@"ios" forKey:@"mobileOS"]; + [params setValue:platform forKey:@"mobileDeviceName"]; + [params setValue:BV_SDK_VERSION forKey:@"bvSDKVersion"]; + + return params; } #pragma mark - Application state change updates - (void)registerForAppStateChanges { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - [self setUpApplicationDidFinishLaunching]; - [self setUpApplicationDidBecomeActive]; - [self setUpApplicationDidEnterBackground]; - }); + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [self setUpApplicationDidFinishLaunching]; + [self setUpApplicationDidBecomeActive]; + [self setUpApplicationDidEnterBackground]; + }); } - (void)setUpApplicationDidFinishLaunching { - [[NSNotificationCenter defaultCenter] - addObserverForName:UIApplicationDidFinishLaunchingNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *note) { - [self sendAppLaunchedEvent:note.userInfo]; - }]; + [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidFinishLaunchingNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note) { + [self sendAppLaunchedEvent:note.userInfo]; + }]; } - (void)setUpApplicationDidBecomeActive { - [[NSNotificationCenter defaultCenter] - addObserverForName:UIApplicationDidBecomeActiveNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *note) { - [self sendAppActiveEvent:note.userInfo]; - }]; + [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note) { + [self sendAppActiveEvent:note.userInfo]; + }]; } - (void)setUpApplicationDidEnterBackground { - [[NSNotificationCenter defaultCenter] - addObserverForName:UIApplicationDidEnterBackgroundNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *note) { - [self sendAppInBackgroundEvent:note.userInfo]; - }]; + [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note) { + [self sendAppInBackgroundEvent:note.userInfo]; + }]; } #pragma mark - App lifecycle events - (NSDictionary *)getAppStateEventParams { - return @{ - @"cl" : @"Lifecycle", - @"type" : @"MobileApp", - @"source" : @"mobile-lifecycle" - }; + return @{ + @"cl" : @"Lifecycle", + @"type" : @"MobileApp", + @"source" : @"mobile-lifecycle" + }; } - (void)sendAppLaunchedEvent:(NSDictionary *)userInfo { - NSDictionary *additionalContext = @{@"appSubState" : @"user-initiated"}; - - if (userInfo) { - do { - - if ([userInfo objectForKey:UIApplicationLaunchOptionsURLKey]) { - additionalContext = @{@"appSubState" : @"url-initiated"}; - break; - } - - if ([userInfo - objectForKey:UIApplicationLaunchOptionsSourceApplicationKey]) { - additionalContext = @{@"appSubState" : @"other-app-initiated"}; - break; - } - - if ([userInfo - objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) { - additionalContext = - @{@"appSubState" : @"remote-notification-initiated"}; - break; - } - - if ([userInfo - objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]) { - additionalContext = @{@"appSubState" : @"local-notification-initiated"}; - break; - } - - } while (NO); - } + NSDictionary *additionalContext = @{ @"appSubState" : @"user-initiated" }; + + if (userInfo) { + do { + if ([userInfo objectForKey:UIApplicationLaunchOptionsURLKey]) { + additionalContext = @{ @"appSubState" : @"url-initiated" }; + break; + } + + if ([userInfo objectForKey: + UIApplicationLaunchOptionsSourceApplicationKey]) { + additionalContext = + @{ @"appSubState" : @"other-app-initiated" }; + break; + } + + if ([userInfo + objectForKey: + UIApplicationLaunchOptionsRemoteNotificationKey]) { + additionalContext = + @{ @"appSubState" : @"remote-notification-initiated" }; + break; + } + + if ([userInfo objectForKey: + UIApplicationLaunchOptionsLocalNotificationKey]) { + additionalContext = + @{ @"appSubState" : @"local-notification-initiated" }; + break; + } + + } while (NO); + } - [self sendAppStateEvent:@"launched" appSubState:additionalContext]; + [self sendAppStateEvent:@"launched" appSubState:additionalContext]; } - (void)sendAppActiveEvent:(NSDictionary *)userInfo { - [self sendAppStateEvent:@"active"]; + [self sendAppStateEvent:@"active"]; } - (void)sendAppInBackgroundEvent:(NSDictionary *)userInfo { - [self sendAppStateEvent:@"background"]; + [self sendAppStateEvent:@"background"]; } - (void)sendAppStateEvent:(NSString *)appState { - [self sendAppStateEvent:appState appSubState:nil]; + [self sendAppStateEvent:appState appSubState:nil]; } - (void)sendAppStateEvent:(NSString *)appState appSubState:(NSDictionary *)additionalContext { - // build param dictionary + // build param dictionary - NSMutableDictionary *eventData = [NSMutableDictionary - dictionaryWithDictionary:[[BVAnalyticEventManager sharedManager] - getCommonAnalyticsDictAnonymous:NO]]; - [eventData addEntriesFromDictionary:[self getMobileDiagnosticParams]]; - [eventData addEntriesFromDictionary:[self getAppStateEventParams]]; - [eventData setObject:appState forKey:@"appState"]; + NSMutableDictionary *eventData = [NSMutableDictionary + dictionaryWithDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; + [eventData addEntriesFromDictionary:[self getMobileDiagnosticParams]]; + [eventData addEntriesFromDictionary:[self getAppStateEventParams]]; + [eventData setObject:appState forKey:@"appState"]; - if (additionalContext) { - [eventData addEntriesFromDictionary:additionalContext]; - } + if (additionalContext) { + [eventData addEntriesFromDictionary:additionalContext]; + } - // send request - [self queueEvent:eventData]; + // send request + [self queueEvent:eventData]; } #pragma mark - Event Timer - (void)setTimer { - dispatch_sync(self.timerEventQueue, ^{ - if (nil == self.queueFlushTimer) { - self.queueFlushTimer = dispatch_source_create( - DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.concurrentEventQueue); - - NSAssert(self.queueFlushTimer, - @"dispatch_source_t timer wasn't allocated properly."); - - if (self.queueFlushTimer) { - dispatch_source_set_timer( - self.queueFlushTimer, - dispatch_time(DISPATCH_TIME_NOW, - self.queueFlushInterval * NSEC_PER_SEC), - DISPATCH_TIME_FOREVER, 0 * NSEC_PER_SEC); - dispatch_source_set_event_handler(self.queueFlushTimer, ^{ - [self flushQueue]; - }); - dispatch_resume(self.queueFlushTimer); + dispatch_sync(self.timerEventQueue, ^{ + if (nil == self.queueFlushTimer) { + self.queueFlushTimer = dispatch_source_create( + DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.concurrentEventQueue); + + NSAssert(self.queueFlushTimer, + @"dispatch_source_t timer wasn't allocated properly."); + + if (self.queueFlushTimer) { + dispatch_source_set_timer( + self.queueFlushTimer, + dispatch_time(DISPATCH_TIME_NOW, + self.queueFlushInterval * NSEC_PER_SEC), + DISPATCH_TIME_FOREVER, 0 * NSEC_PER_SEC); + dispatch_source_set_event_handler(self.queueFlushTimer, ^{ + [self flushQueue]; + }); + dispatch_resume(self.queueFlushTimer); + } } - } - }); + }); } - (void)invalidateTimer { - dispatch_sync(self.timerEventQueue, ^{ - if (nil != self.queueFlushTimer) { - dispatch_source_cancel(self.queueFlushTimer); - self.queueFlushTimer = nil; - } - }); + dispatch_sync(self.timerEventQueue, ^{ + if (nil != self.queueFlushTimer) { + dispatch_source_cancel(self.queueFlushTimer); + self.queueFlushTimer = nil; + } + }); } #pragma mark - Event Queueing - (void)queueEvent:(NSDictionary *)eventData { - [[BVLogger sharedLogger] - analyticsMessage:[NSString - stringWithFormat:@"%@ - %@ - %@", - [eventData objectForKey:@"cl"], - [eventData objectForKey:@"type"], - [eventData objectForKey:@"name"]]]; - [self processEvent:eventData isAnonymous:NO]; + [[BVLogger sharedLogger] + analyticsMessage:[NSString + stringWithFormat:@"%@ - %@ - %@", + [eventData objectForKey:@"cl"], + [eventData objectForKey:@"type"], + [eventData + objectForKey:@"name"]]]; + [self processEvent:eventData isAnonymous:NO]; } - (void)queueAnonymousEvent:(NSDictionary *)eventData { - [self processEvent:eventData isAnonymous:YES]; + [self processEvent:eventData isAnonymous:YES]; } - (void)processEvent:(NSDictionary *)eventData isAnonymous:(BOOL)anonymous { - NSMutableDictionary *eventForQueue = - [NSMutableDictionary dictionaryWithDictionary:eventData]; - [eventForQueue - addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] - getCommonAnalyticsDictAnonymous:anonymous]]; + NSMutableDictionary *eventForQueue = + [NSMutableDictionary dictionaryWithDictionary:eventData]; + [eventForQueue addEntriesFromDictionary: + [[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:anonymous]]; - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - // Update event queue - [self.eventQueue addObject:eventForQueue]; - }); + dispatch_barrier_sync(self.concurrentEventQueue, ^{ + // Update event queue + [self.eventQueue addObject:eventForQueue]; + }); - [self scheduleEventQueueFlush]; + [self scheduleEventQueueFlush]; } - (void)scheduleEventQueueFlush { - // schedule a queue flush, if not already scheduled - // many times, a rush of events come very close to each other, and then - // things are quiet for a while. + // schedule a queue flush, if not already scheduled + // many times, a rush of events come very close to each other, and then + // things are quiet for a while. - dispatch_async(self.concurrentEventQueue, ^{ - [self setTimer]; - }); + dispatch_async(self.concurrentEventQueue, ^{ + [self setTimer]; + }); } - (void)queuePageViewEventDict:(NSDictionary *)pageViewEvent { - NSMutableDictionary *eventForQueue = - [NSMutableDictionary dictionaryWithDictionary:pageViewEvent]; - [eventForQueue - addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] - getCommonAnalyticsDictAnonymous:NO]]; + NSMutableDictionary *eventForQueue = + [NSMutableDictionary dictionaryWithDictionary:pageViewEvent]; + [eventForQueue + addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - // Update PageView queue events - [self.pageviewQueue addObject:eventForQueue]; - }); + dispatch_barrier_sync(self.concurrentEventQueue, ^{ + // Update PageView queue events + [self.pageviewQueue addObject:eventForQueue]; + }); - [self flushQueue]; + [self flushQueue]; } - (void)flushQueue { - dispatch_barrier_async(self.concurrentEventQueue, ^{ - [self flushQueueUnsafe]; - }); + dispatch_barrier_async(self.concurrentEventQueue, ^{ + [self flushQueueUnsafe]; + }); } // Flush queue for POST batch - (void)flushQueueUnsafe { - // clear flush timer - [self invalidateTimer]; - - // send events - if ([self.eventQueue count] > 0) { - - NSDictionary *batch = @{@"batch" : self.eventQueue}; - [self - sendBatchedPOSTEvent:batch - withCompletionHandler:^(NSError *__nullable error) { - if (error) { - [[BVLogger sharedLogger] - error:[NSString stringWithFormat: + // clear flush timer + [self invalidateTimer]; + + // send events + if ([self.eventQueue count] > 0) { + NSDictionary *batch = @{ @"batch" : self.eventQueue }; + [self + sendBatchedPOSTEvent:batch + withCompletionHandler:^(NSError *__nullable error) { + if (error) { + [[BVLogger sharedLogger] + error:[NSString + stringWithFormat: @"ERROR: posting magpie impression event%@", error.localizedDescription]]; - } - - [self flushImpressionEventCompletionQueue]; + } - }]; + [self flushImpressionEventCompletionQueue]; - // purge queue - [self.eventQueue removeAllObjects]; - } + }]; - // send page view events - if ([self.pageviewQueue count] > 0) { + // purge queue + [self.eventQueue removeAllObjects]; + } - NSDictionary *batch = @{@"batch" : self.pageviewQueue}; - [self sendBatchedPOSTEvent:batch - withCompletionHandler:^(NSError *__nullable error) { - if (error) { - [[BVLogger sharedLogger] - error:[NSString stringWithFormat: + // send page view events + if ([self.pageviewQueue count] > 0) { + NSDictionary *batch = @{ @"batch" : self.pageviewQueue }; + [self sendBatchedPOSTEvent:batch + withCompletionHandler:^(NSError *__nullable error) { + if (error) { + [[BVLogger sharedLogger] + error:[NSString + stringWithFormat: @"ERROR: posting magpie pageview event%@", error.localizedDescription]]; - } + } - [self flushPageViewEventCompletionQueue]; + [self flushPageViewEventCompletionQueue]; - }]; + }]; - // purge pageview queue - [self.pageviewQueue removeAllObjects]; - } + // purge pageview queue + [self.pageviewQueue removeAllObjects]; + } } - (void)sendBatchedPOSTEvent:(NSDictionary *)eventData withCompletionHandler: (void (^)(NSError *__nullable error))completionHandler { - NSURL *url = [NSURL URLWithString:[self baseUrl]]; - [[BVLogger sharedLogger] - analyticsMessage:[NSString - stringWithFormat:@"POST Event: %@\nWith Data:%@", - url, eventData]]; - - if (_isDryRunAnalytics) { + NSURL *url = [NSURL URLWithString:[self baseUrl]]; [[BVLogger sharedLogger] - info:@"Analytic events are not being sent to server"]; - return; - } - - /// For private classes we ask for the NSURLSession but we don't hand back any - /// objects since it would be useless to the developers as they have no - /// interface to the object graph. - NSURLSession *session = nil; - id sessionDelegate = - [BVSDKManager sharedManager].urlSessionDelegate; - if (sessionDelegate && - [sessionDelegate respondsToSelector:@selector(URLSessionForBVObject:)]) { - session = [sessionDelegate URLSessionForBVObject:nil]; - } - - session = session ?: [BVNetworkingManager sharedManager].bvNetworkingSession; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - request.HTTPMethod = @"POST"; - [request setValue:@"application/json" - forHTTPHeaderField:@"Content-Type"]; // content type - - NSError *error = nil; - NSData *data = [NSJSONSerialization dataWithJSONObject:eventData - options:kNilOptions - error:&error]; - - if (!error) { - NSURLSessionUploadTask *postTask = [session - uploadTaskWithRequest:request - fromData:data - completionHandler:^(NSData *data, NSURLResponse *response, - NSError *error) { - - // completion - // one-way communication, client->server: disregard results - // but log error, if any - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if ((httpResponse && httpResponse.statusCode >= 300) || - error != nil) { - if (data) { - NSString *errorMsg = - [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - [[BVLogger sharedLogger] error:errorMsg]; - - } else { - [[BVLogger sharedLogger] - error:[NSString - stringWithFormat:@"ERROR: Posting " - @"analytics event " - @"failed with " - @"status: %ld and " - @"error: %@", - (long)httpResponse.statusCode, - error]]; - } - - } else { - // Successful analyatics event sent - NSString *message = [NSString - stringWithFormat:@"Analytics event sent successfully."]; - [[BVLogger sharedLogger] analyticsMessage:message]; - } - - dispatch_barrier_async(self.concurrentEventQueue, ^{ - completionHandler(error); - }); + analyticsMessage:[NSString + stringWithFormat:@"POST Event: %@\nWith Data:%@", + url, eventData]]; + + if (_isDryRunAnalytics) { + [[BVLogger sharedLogger] + info:@"Analytic events are not being sent to server"]; + return; + } - }]; + /// For private classes we ask for the NSURLSession but we don't hand back + /// any + /// objects since it would be useless to the developers as they have no + /// interface to the object graph. + NSURLSession *session = nil; + id sessionDelegate = + [BVSDKManager sharedManager].urlSessionDelegate; + if (sessionDelegate && + [sessionDelegate + respondsToSelector:@selector(URLSessionForBVObject:)]) { + session = [sessionDelegate URLSessionForBVObject:nil]; + } - [postTask resume]; - }; + session = + session ?: [BVNetworkingManager sharedManager].bvNetworkingSession; + + NSMutableURLRequest *request = + [[NSMutableURLRequest alloc] initWithURL:url]; + request.HTTPMethod = @"POST"; + [request setValue:@"application/json" + forHTTPHeaderField:@"Content-Type"]; // content type + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:eventData + options:kNilOptions + error:&error]; + + if (!error) { + NSURLSessionUploadTask *postTask = [session + uploadTaskWithRequest:request + fromData:data + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *error) { + + // completion + // one-way communication, client->server: disregard results + // but log error, if any + NSHTTPURLResponse *httpResponse = + (NSHTTPURLResponse *)response; + if ((httpResponse && httpResponse.statusCode >= 300) || + error != nil) { + if (data) { + NSString *errorMsg = [[NSString alloc] + initWithData:data + encoding:NSUTF8StringEncoding]; + [[BVLogger sharedLogger] error:errorMsg]; + + } else { + [[BVLogger sharedLogger] + error:[NSString + stringWithFormat:@"ERROR: Posting " + @"analytics event " + @"failed with " + @"status: %ld and " + @"error: %@", + (long)httpResponse + .statusCode, + error]]; + } + + } else { + // Successful analyatics event sent + NSString *message = + [NSString stringWithFormat: + @"Analytics event sent successfully."]; + [[BVLogger sharedLogger] analyticsMessage:message]; + } + + dispatch_barrier_async(self.concurrentEventQueue, ^{ + completionHandler(error); + }); + + }]; + + [postTask resume]; + }; } #pragma mark - NSLocale Analytic Handling - (NSLocale *)analyticsLocale { - __block NSLocale *locale = nil; - dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ - locale = _analyticsLocale; - }); - return locale; + __block NSLocale *locale = nil; + dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ + locale = self->_analyticsLocale; + }); + return locale; } - (void)setAnalyticsLocale:(NSLocale *)analyticsLocale { - dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ - _analyticsLocale = analyticsLocale; - - if (_analyticsLocale) { - /// Turn off locale state changes - [self unregisterForCurrentLocaleDidChangeNotifications]; - } else { - _analyticsLocale = [NSLocale autoupdatingCurrentLocale]; - /// Turn on locale state changes - [self registerForCurrentLocaleDidChangeNotifications]; - } + dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ + self->_analyticsLocale = analyticsLocale; + + if (self->_analyticsLocale) { + /// Turn off locale state changes + [self unregisterForCurrentLocaleDidChangeNotifications]; + } else { + self->_analyticsLocale = [NSLocale autoupdatingCurrentLocale]; + /// Turn on locale state changes + [self registerForCurrentLocaleDidChangeNotifications]; + } - [self logAnalyticsLocaleUnsafe]; - }); + [self logAnalyticsLocaleUnsafe]; + }); } - (void)logAnalyticsLocale { - dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ - [self logAnalyticsLocaleUnsafe]; - }); + dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ + [self logAnalyticsLocaleUnsafe]; + }); } - (void)logAnalyticsLocaleUnsafe { - NSString *logLocale = nil; - if (_analyticsLocale) { - logLocale = - ((NSString *)[_analyticsLocale objectForKey:NSLocaleCountryCode]) - .uppercaseString; - } + NSString *logLocale = nil; + if (_analyticsLocale) { + logLocale = + ((NSString *)[_analyticsLocale objectForKey:NSLocaleCountryCode]) + .uppercaseString; + } - [[BVLogger sharedLogger] - analyticsMessage:[NSString - stringWithFormat:@"Configuration has set Locale: %@", - logLocale]]; + [[BVLogger sharedLogger] + analyticsMessage: + [NSString stringWithFormat:@"Configuration has set Locale: %@", + logLocale]]; } - (void)registerForCurrentLocaleDidChangeNotifications { - if (!self.localeUpdateNotificationCenterToken) { - [[BVLogger sharedLogger] - analyticsMessage: - @"Analytics REGISTERING for Locale Change Notifications."]; + if (!self.localeUpdateNotificationCenterToken) { + [[BVLogger sharedLogger] + analyticsMessage: + @"Analytics REGISTERING for Locale Change Notifications."]; - self.localeUpdateNotificationCenterToken = - [[NSNotificationCenter defaultCenter] + self.localeUpdateNotificationCenterToken = [ + [NSNotificationCenter defaultCenter] addObserverForName:NSCurrentLocaleDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { dispatch_sync(self.localeUpdateNotificationTokenQueue, ^{ - _analyticsLocale = [NSLocale autoupdatingCurrentLocale]; + self->_analyticsLocale = + [NSLocale autoupdatingCurrentLocale]; [self logAnalyticsLocaleUnsafe]; }); }]; - } + } } - (void)unregisterForCurrentLocaleDidChangeNotifications { + [[BVLogger sharedLogger] + analyticsMessage: + @"Analytics UNREGISTERING for Locale Change Notifications."]; - [[BVLogger sharedLogger] - analyticsMessage: - @"Analytics UNREGISTERING for Locale Change Notifications."]; - - if (self.localeUpdateNotificationCenterToken) { - [[NSNotificationCenter defaultCenter] - removeObserver:self.localeUpdateNotificationCenterToken]; - self.localeUpdateNotificationCenterToken = nil; - } + if (self.localeUpdateNotificationCenterToken) { + [[NSNotificationCenter defaultCenter] + removeObserver:self.localeUpdateNotificationCenterToken]; + self.localeUpdateNotificationCenterToken = nil; + } } #pragma mark - Helpers - (NSString *)formatDate:(NSDate *)date { - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - NSLocale *enUSPOSIXLocale = - [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; - [dateFormatter setLocale:enUSPOSIXLocale]; - [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + NSLocale *enUSPOSIXLocale = + [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [dateFormatter setLocale:enUSPOSIXLocale]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; - return [dateFormatter - stringFromDate:date]; // return ISO-8601 formatted UTC timestamp + return [dateFormatter + stringFromDate:date]; // return ISO-8601 formatted UTC timestamp } - (NSString *)baseUrl { - BVLocaleServiceManager *localeServiceManager = - [BVLocaleServiceManager sharedManager]; - NSAssert(localeServiceManager, @"BVLocaleServiceManager is nil."); - - if (self.isStagingServer) { - [[BVLogger sharedLogger] - error:@"WARNING: Using staging server for analytic events. This should " - @"only be enabled for non-production."]; - } + BVLocaleServiceManager *localeServiceManager = + [BVLocaleServiceManager sharedManager]; + NSAssert(localeServiceManager, @"BVLocaleServiceManager is nil."); + + if (self.isStagingServer) { + [[BVLogger sharedLogger] error:@"WARNING: Using staging server for " + @"analytic events. This should " + @"only be enabled for non-production."]; + } - return [localeServiceManager - resourceForService:BVLocaleServiceManagerServiceAnalytics - withLocale:self.analyticsLocale - andIsProduction:(!self.isStagingServer)]; + return [localeServiceManager + resourceForService:BVLocaleServiceManagerServiceAnalytics + withLocale:self.analyticsLocale + andIsProduction:(!self.isStagingServer)]; } #pragma mark - Testing - (void)enqueueImpressionTestWithName:(NSString *)testName withCompletionBlock:(dispatch_block_t)completionBlock { + dispatch_barrier_sync(self.concurrentEventQueue, ^{ - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - - dispatch_block_t opaqueCompletion = - self.testImpressionEventCompletionQueue[testName]; - if (opaqueCompletion) { - opaqueCompletion(); - } + dispatch_block_t opaqueCompletion = + self.testImpressionEventCompletionQueue[testName]; + if (opaqueCompletion) { + opaqueCompletion(); + } - self.testImpressionEventCompletionQueue[testName] = completionBlock; + self.testImpressionEventCompletionQueue[testName] = completionBlock; - NSAssert( - 2 > self.testImpressionEventCompletionQueue.allKeys.count, - @"Attempting to run more than one test, probably a race condition."); - }); + NSAssert( + 2 > self.testImpressionEventCompletionQueue.allKeys.count, + @"Attempting to run more than one test, probably a race condition."); + }); } - (void)enqueuePageViewTestWithName:(NSString *)testName withCompletionBlock:(dispatch_block_t)completionBlock { + dispatch_barrier_sync(self.concurrentEventQueue, ^{ - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - - dispatch_block_t opaqueCompletion = - self.testPageViewEventCompletionQueue[testName]; - if (opaqueCompletion) { - opaqueCompletion(); - } + dispatch_block_t opaqueCompletion = + self.testPageViewEventCompletionQueue[testName]; + if (opaqueCompletion) { + opaqueCompletion(); + } - self.testPageViewEventCompletionQueue[testName] = completionBlock; + self.testPageViewEventCompletionQueue[testName] = completionBlock; - NSAssert( - 2 > self.testPageViewEventCompletionQueue.allKeys.count, - @"Attempting to run more than one test, probably a race condition."); - }); + NSAssert( + 2 > self.testPageViewEventCompletionQueue.allKeys.count, + @"Attempting to run more than one test, probably a race condition."); + }); } - (void)flushImpressionEventCompletionQueue { + dispatch_barrier_async(self.concurrentEventQueue, ^{ - dispatch_barrier_async(self.concurrentEventQueue, ^{ - - if (!_testImpressionEventCompletionQueue) { - return; - } + if (!self->_testImpressionEventCompletionQueue) { + return; + } - dispatch_block_t opaqueCompletion = - self.testImpressionEventCompletionQueue.allValues.firstObject; - if (opaqueCompletion) { - opaqueCompletion(); - } + dispatch_block_t opaqueCompletion = + self.testImpressionEventCompletionQueue.allValues.firstObject; + if (opaqueCompletion) { + opaqueCompletion(); + } - [self.testImpressionEventCompletionQueue removeAllObjects]; - }); + [self.testImpressionEventCompletionQueue removeAllObjects]; + }); } - (void)flushPageViewEventCompletionQueue { + dispatch_barrier_async(self.concurrentEventQueue, ^{ - dispatch_barrier_async(self.concurrentEventQueue, ^{ - - if (!_testPageViewEventCompletionQueue) { - return; - } + if (!self->_testPageViewEventCompletionQueue) { + return; + } - dispatch_block_t opaqueCompletion = - self.testPageViewEventCompletionQueue.allValues.firstObject; - if (opaqueCompletion) { - opaqueCompletion(); - } + dispatch_block_t opaqueCompletion = + self.testPageViewEventCompletionQueue.allValues.firstObject; + if (opaqueCompletion) { + opaqueCompletion(); + } - [self.testPageViewEventCompletionQueue removeAllObjects]; - }); + [self.testPageViewEventCompletionQueue removeAllObjects]; + }); } @end diff --git a/BVSDK/BVCommon/BVSDKManager.m b/BVSDK/BVCommon/BVSDKManager.m index 59a175ea..96a29e80 100644 --- a/BVSDK/BVCommon/BVSDKManager.m +++ b/BVSDK/BVCommon/BVSDKManager.m @@ -13,18 +13,18 @@ #import @interface BVSDKManager () -@property(nonatomic, strong) BVSDKConfiguration *configuration; -@property(nonatomic, strong) dispatch_queue_t urlSessionDelegateQueue; -@property(nonnull, nonatomic, strong) NSString *clientId; -@property(nonatomic, assign) BOOL staging; -@property(nonnull, nonatomic, strong) NSString *apiKeyConversations; -@property(nonnull, nonatomic, strong) NSString *apiKeyConversationsStores; -@property(nonnull, nonatomic, strong) NSString *apiKeyPIN; -@property(nonnull, nonatomic, strong) +@property (nonatomic, strong) BVSDKConfiguration *configuration; +@property (nonatomic, strong) dispatch_queue_t urlSessionDelegateQueue; +@property (nonnull, nonatomic, strong) NSString *clientId; +@property (nonatomic, assign) BOOL staging; +@property (nonnull, nonatomic, strong) NSString *apiKeyConversations; +@property (nonnull, nonatomic, strong) NSString *apiKeyConversationsStores; +@property (nonnull, nonatomic, strong) NSString *apiKeyPIN; +@property (nonnull, nonatomic, strong) NSString *storeReviewContentExtensionCategory; -@property(nonnull, nonatomic, strong) NSString *PINContentExtensionCategory; -@property(nonnull, nonatomic, strong) NSString *apiKeyShopperAdvertising; -@property(nonnull, nonatomic, strong) NSString *apiKeyCurations; +@property (nonnull, nonatomic, strong) NSString *PINContentExtensionCategory; +@property (nonnull, nonatomic, strong) NSString *apiKeyShopperAdvertising; +@property (nonnull, nonatomic, strong) NSString *apiKeyCurations; @end @implementation BVSDKManager @@ -37,305 +37,307 @@ @implementation BVSDKManager urlSessionDelegateQueue = _urlSessionDelegateQueue; - (nullable id)urlSessionDelegate { - __block id tempDelegate = nil; - dispatch_sync(self.urlSessionDelegateQueue, ^{ - tempDelegate = _urlSessionDelegate; - }); - return tempDelegate; + __block id tempDelegate = nil; + dispatch_sync(self.urlSessionDelegateQueue, ^{ + tempDelegate = self->_urlSessionDelegate; + }); + return tempDelegate; } - (void)setUrlSessionDelegate: (nullable id)urlSessionDelegate { - dispatch_sync(self.urlSessionDelegateQueue, ^{ - _urlSessionDelegate = urlSessionDelegate; - }); + dispatch_sync(self.urlSessionDelegateQueue, ^{ + self->_urlSessionDelegate = urlSessionDelegate; + }); } + (void)configure:(BVConfigurationType)configurationType { - [[self sharedManager] attemptToLoadConfiguration:configurationType]; + [[self sharedManager] attemptToLoadConfiguration:configurationType]; } + (void)configureWithConfiguration:(NSDictionary *)configDict configType:(BVConfigurationType)configType { - [[self sharedManager] configureWithDictionary:configDict - configType:configType]; + [[self sharedManager] configureWithDictionary:configDict + configType:configType]; } + (instancetype)sharedManager { - // structure used to test whether the block has completed or not - static dispatch_once_t p = 0; + // structure used to test whether the block has completed or not + static dispatch_once_t p = 0; - // initialize sharedObject as nil (first call only) - __strong static id _sharedObject = nil; + // initialize sharedObject as nil (first call only) + __strong static id _sharedObject = nil; - // executes a block object once and only once for the lifetime of an - // application - dispatch_once(&p, ^{ - _sharedObject = [[self alloc] init]; - }); + // executes a block object once and only once for the lifetime of an + // application + dispatch_once(&p, ^{ + _sharedObject = [[self alloc] init]; + }); - // returns the same object each time - return _sharedObject; + // returns the same object each time + return _sharedObject; } - (instancetype)init { - if ((self = [super init])) { - _bvUser = [[BVAuthenticatedUser alloc] init]; - - // make sure analytics has been started - [BVAnalyticsManager sharedManager]; - - _urlSessionDelegateQueue = dispatch_queue_create( - "com.bazaarvoice.BVSDKManager.urlSessionDelegateQueue", - DISPATCH_QUEUE_SERIAL); - - _timeout = 60; - _staging = NO; - _clientId = nil; - _apiKeyConversations = nil; - _apiKeyShopperAdvertising = nil; - _apiKeyConversationsStores = nil; - _configuration = [[BVSDKConfiguration alloc] init]; - } - return self; + if ((self = [super init])) { + _bvUser = [[BVAuthenticatedUser alloc] init]; + + // make sure analytics has been started + [BVAnalyticsManager sharedManager]; + + _urlSessionDelegateQueue = dispatch_queue_create( + "com.bazaarvoice.BVSDKManager.urlSessionDelegateQueue", + DISPATCH_QUEUE_SERIAL); + + _timeout = 60; + _staging = NO; + _clientId = nil; + _apiKeyConversations = nil; + _apiKeyShopperAdvertising = nil; + _apiKeyConversationsStores = nil; + _configuration = [[BVSDKConfiguration alloc] init]; + } + return self; } - (void)attemptToLoadConfiguration:(BVConfigurationType)configType { - NSString *fileName; - if (configType == BVConfigurationTypeProd) { - fileName = BVSDKConfigFileNameProd; - } else { - fileName = BVSDKConfigFileNameStaging; - } - - NSString *filePath = - [[NSBundle mainBundle] pathForResource:fileName - ofType:BVSDKConfigFileExt]; - NSAssert( - filePath, - @"File %@.%@ could not be found in bundle. Unable to configure bundle", - fileName, BVSDKConfigFileExt); - - if (filePath) { - NSData *data = [NSData dataWithContentsOfFile:filePath]; - NSError *error; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&error]; - [self configureWithDictionary:json configType:configType]; - } - - [self assertConfiguration]; + NSString *fileName; + if (configType == BVConfigurationTypeProd) { + fileName = BVSDKConfigFileNameProd; + } else { + fileName = BVSDKConfigFileNameStaging; + } + + NSString *filePath = + [[NSBundle mainBundle] pathForResource:fileName + ofType:BVSDKConfigFileExt]; + NSAssert( + filePath, + @"File %@.%@ could not be found in bundle. Unable to configure bundle", + fileName, BVSDKConfigFileExt); + + if (filePath) { + NSData *data = [NSData dataWithContentsOfFile:filePath]; + NSError *error; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&error]; + [self configureWithDictionary:json configType:configType]; + } + + [self assertConfiguration]; } - (void)configureWithDictionary:(NSDictionary *)dict configType:(BVConfigurationType)configType { - NSMutableDictionary *config = dict.mutableCopy; - [config setObject:@(configType == BVConfigurationTypeStaging) - forKey:@"staging"]; - _configuration = [[BVSDKConfiguration alloc] initWithDictionary:dict - configType:configType]; - [BVAnalyticsManager sharedManager].isDryRunAnalytics = - _configuration.dryRunAnalytics; - - /// Handle Analytics Locale Configuration - NSLocale *analyticsLocale = nil; - NSString *analyticsLocaleIdentifier = - _configuration.analyticsLocaleIdentifier; - - if (analyticsLocaleIdentifier) { - analyticsLocale = - [[NSLocale alloc] initWithLocaleIdentifier:analyticsLocaleIdentifier]; - } else { - [[BVLogger sharedLogger] - warning:[NSString stringWithFormat:@"BVSDK is currently using user " - @"region settings. Please see the " - @"documentation regarding setting " - @"proper locale settings for " - @"dealing with user data privacy."]]; - } - - [BVAnalyticsManager sharedManager].analyticsLocale = analyticsLocale; - - [self copyJson:config toObj:self]; + NSMutableDictionary *config = dict.mutableCopy; + [config setObject:@(configType == BVConfigurationTypeStaging) + forKey:@"staging"]; + _configuration = [[BVSDKConfiguration alloc] initWithDictionary:dict + configType:configType]; + [BVAnalyticsManager sharedManager].isDryRunAnalytics = + _configuration.dryRunAnalytics; + + /// Handle Analytics Locale Configuration + NSLocale *analyticsLocale = nil; + NSString *analyticsLocaleIdentifier = + _configuration.analyticsLocaleIdentifier; + + if (analyticsLocaleIdentifier) { + analyticsLocale = [[NSLocale alloc] + initWithLocaleIdentifier:analyticsLocaleIdentifier]; + } else { + [[BVLogger sharedLogger] + warning:[NSString + stringWithFormat:@"BVSDK is currently using user " + @"region settings. Please see the " + @"documentation regarding setting " + @"proper locale settings for " + @"dealing with user data privacy."]]; + } + + [BVAnalyticsManager sharedManager].analyticsLocale = analyticsLocale; + + [self copyJson:config toObj:self]; } - (void)setConfiguration:(nonnull BVSDKConfiguration *)configuration { - _configuration = configuration; + _configuration = configuration; } - (void)copyJson:(NSDictionary *)json toObj:(NSObject *)obj { - for (NSString *key in json.allKeys) { - if ([obj respondsToSelector:NSSelectorFromString(key)]) { - [obj setValue:json[key] forKeyPath:key]; + for (NSString *key in json.allKeys) { + if ([obj respondsToSelector:NSSelectorFromString(key)]) { + [obj setValue:json[key] forKeyPath:key]; + } } - } } - (NSString *)description { - NSString *returnValue = [NSString - stringWithFormat:@"Setting Values:\n conversations API key = %@ \n " - @"shopper marketing API key = %@ \n conversations " - @"for stores API key = %@ \n BVSDK Version = %@ \n " - @"clientId = %@ \n staging = %i \n", - self.configuration.apiKeyConversations, - self.configuration.apiKeyShopperAdvertising, - self.configuration.apiKeyConversationsStores, - BV_SDK_VERSION, self.configuration.clientId, - self.configuration.staging]; - - return returnValue; + NSString *returnValue = [NSString + stringWithFormat:@"Setting Values:\n conversations API key = %@ \n " + @"shopper marketing API key = %@ \n conversations " + @"for stores API key = %@ \n BVSDK Version = %@ \n " + @"clientId = %@ \n staging = %i \n", + self.configuration.apiKeyConversations, + self.configuration.apiKeyShopperAdvertising, + self.configuration.apiKeyConversationsStores, + BV_SDK_VERSION, self.configuration.clientId, + self.configuration.staging]; + + return returnValue; } - (NSString *)urlRootShopperAdvertising { - return self.configuration.staging ? @"https://my.network-stg.bazaarvoice.com" - : @"https://my.network.bazaarvoice.com"; + return self.configuration.staging + ? @"https://my.network-stg.bazaarvoice.com" + : @"https://my.network.bazaarvoice.com"; } // SDK supports only a single client ID - (void)setClientId:(NSString *)clientId { - _clientId = clientId; - [BVAnalyticEventManager sharedManager].clientId = clientId; - [BVAnalyticEventManager sharedManager].eventSource = @"native-mobile-sdk"; - [self assertConfiguration]; - [_configuration setValue:clientId forKeyPath:@"clientId"]; + _clientId = clientId; + [BVAnalyticEventManager sharedManager].clientId = clientId; + [BVAnalyticEventManager sharedManager].eventSource = @"native-mobile-sdk"; + [self assertConfiguration]; + [_configuration setValue:clientId forKeyPath:@"clientId"]; } - (void)assertConfiguration { - NSAssert(_clientId && 0 < _clientId.length, - @"You must supply valid client id in the " - @"BVSDKManager before using the Bazaarvoice " - @"SDK."); + NSAssert(_clientId && 0 < _clientId.length, + @"You must supply valid client id in the " + @"BVSDKManager before using the Bazaarvoice " + @"SDK."); } // SDK supports only a single setting for production or stage - (void)setStaging:(BOOL)staging { - _staging = staging; - [BVAnalyticsManager sharedManager].isStagingServer = staging; - [_configuration setValue:@(staging) forKeyPath:@"staging"]; + _staging = staging; + [BVAnalyticsManager sharedManager].isStagingServer = staging; + [_configuration setValue:@(staging) forKeyPath:@"staging"]; } - (void)setApiKeyShopperAdvertising:(NSString *)apiKeyShopperAdvertising { - _apiKeyShopperAdvertising = apiKeyShopperAdvertising; - [_configuration setValue:apiKeyShopperAdvertising - forKeyPath:@"apiKeyShopperAdvertising"]; + _apiKeyShopperAdvertising = apiKeyShopperAdvertising; + [_configuration setValue:apiKeyShopperAdvertising + forKeyPath:@"apiKeyShopperAdvertising"]; } - (void)setApiKeyConversations:(NSString *)apiKeyConversations { - _apiKeyConversations = apiKeyConversations; - [_configuration setValue:apiKeyConversations - forKeyPath:@"apiKeyConversations"]; + _apiKeyConversations = apiKeyConversations; + [_configuration setValue:apiKeyConversations + forKeyPath:@"apiKeyConversations"]; } - (void)setApiKeyConversationsStores:(NSString *)apiKeyConversationsStores { - _apiKeyConversationsStores = apiKeyConversationsStores; - NSDictionary *userInfo = @{ - CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION : _apiKeyConversationsStores - }; - [[NSNotificationCenter defaultCenter] - postNotificationName:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION - object:nil - userInfo:userInfo]; - [_configuration setValue:apiKeyConversationsStores - forKeyPath:@"apiKeyConversationsStores"]; + _apiKeyConversationsStores = apiKeyConversationsStores; + NSDictionary *userInfo = @{ + CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION : + _apiKeyConversationsStores + }; + [[NSNotificationCenter defaultCenter] + postNotificationName:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION + object:nil + userInfo:userInfo]; + [_configuration setValue:apiKeyConversationsStores + forKeyPath:@"apiKeyConversationsStores"]; } - (void)setApiKeyPIN:(NSString *)apiKeyPIN { - _apiKeyPIN = apiKeyPIN; - NSDictionary *userInfo = @{PIN_API_KEY_SET_NOTIFICATION : _apiKeyPIN}; - [[NSNotificationCenter defaultCenter] - postNotificationName:PIN_API_KEY_SET_NOTIFICATION - object:nil - userInfo:userInfo]; - [_configuration setValue:apiKeyPIN forKeyPath:@"apiKeyPIN"]; + _apiKeyPIN = apiKeyPIN; + NSDictionary *userInfo = @{PIN_API_KEY_SET_NOTIFICATION : _apiKeyPIN}; + [[NSNotificationCenter defaultCenter] + postNotificationName:PIN_API_KEY_SET_NOTIFICATION + object:nil + userInfo:userInfo]; + [_configuration setValue:apiKeyPIN forKeyPath:@"apiKeyPIN"]; } - (void)setStoreReviewContentExtensionCategory: (NSString *)storeReviewContentExtensionCategory { - _storeReviewContentExtensionCategory = storeReviewContentExtensionCategory; - [_configuration setValue:storeReviewContentExtensionCategory - forKeyPath:@"storeReviewContentExtensionCategory"]; + _storeReviewContentExtensionCategory = storeReviewContentExtensionCategory; + [_configuration setValue:storeReviewContentExtensionCategory + forKeyPath:@"storeReviewContentExtensionCategory"]; } - (void)setPINContentExtensionCategory:(NSString *)PINContentExtensionCategory { - _PINContentExtensionCategory = PINContentExtensionCategory; - [_configuration setValue:PINContentExtensionCategory - forKeyPath:@"PINContentExtensionCategory"]; + _PINContentExtensionCategory = PINContentExtensionCategory; + [_configuration setValue:PINContentExtensionCategory + forKeyPath:@"PINContentExtensionCategory"]; } - (void)setApiKeyCurations:(NSString *)apiKeyCurations { - _apiKeyCurations = apiKeyCurations; - [_configuration setValue:apiKeyCurations forKeyPath:@"apiKeyCurations"]; + _apiKeyCurations = apiKeyCurations; + [_configuration setValue:apiKeyCurations forKeyPath:@"apiKeyCurations"]; } - (void)setLogLevel:(BVLogLevel)logLevel { - [[BVLogger sharedLogger] setLogLevel:logLevel]; + [[BVLogger sharedLogger] setLogLevel:logLevel]; } #pragma mark - user - (void)setUserWithAuthString:(NSString *)userAuthString { - if (userAuthString && 0 < userAuthString.length) { - [self setUserId:userAuthString]; - } else { - [[BVLogger sharedLogger] error:@"No userAuthString was supplied for " - @"the recommendations manager!"]; - } + if (userAuthString && 0 < userAuthString.length) { + [self setUserId:userAuthString]; + } else { + [[BVLogger sharedLogger] error:@"No userAuthString was supplied for " + @"the recommendations manager!"]; + } } // Update the user profile by calling the /users/ endpoint if the targeting // params are empty. - (void)updateUserProfileIfEmpty { - [self.bvUser updateProfile:false - withAPIKey:self.configuration.apiKeyShopperAdvertising - isStaging:self.configuration.staging]; + [self.bvUser updateProfile:false + withAPIKey:self.configuration.apiKeyShopperAdvertising + isStaging:self.configuration.staging]; } // Udpate the user profile by calling the /users/ endpoint, regardless of the // current state of the targeting params - (void)updateUserProfileForce { - [self.bvUser updateProfile:true - withAPIKey:self.configuration.apiKeyShopperAdvertising - isStaging:self.configuration.staging]; + [self.bvUser updateProfile:true + withAPIKey:self.configuration.apiKeyShopperAdvertising + isStaging:self.configuration.staging]; } - (void)setUserId:(NSString *)userAuthString { - self.bvUser.userAuthString = userAuthString; + self.bvUser.userAuthString = userAuthString; - BVPersonalizationEvent *personEvent = [[BVPersonalizationEvent alloc] - initWithUserAuthenticationString:userAuthString]; - [BVPixel trackEvent:personEvent]; + BVPersonalizationEvent *personEvent = [[BVPersonalizationEvent alloc] + initWithUserAuthenticationString:userAuthString]; + [BVPixel trackEvent:personEvent]; - [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [NSObject cancelPreviousPerformRequestsWithTarget:self]; - // try to grab the profile as soon as its available - [self updateUserProfileForce]; - [self waitForProfileUpdatesByPollingWithStep:6.0f andMaxTimeInterval:24.0f]; + // try to grab the profile as soon as its available + [self updateUserProfileForce]; + [self waitForProfileUpdatesByPollingWithStep:6.0f andMaxTimeInterval:24.0f]; } - (void)waitForProfileUpdatesByPollingWithStep:(NSTimeInterval)step andMaxTimeInterval:(NSTimeInterval)maxTimeInterval { + NSTimeInterval currentStep = step; - NSTimeInterval currentStep = step; + while (currentStep > maxTimeInterval) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, + (int64_t)(currentStep * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [self updateUserProfileIfEmpty]; + }); - while (currentStep > maxTimeInterval) { - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(currentStep * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self updateUserProfileIfEmpty]; - }); - - currentStep += currentStep; - } + currentStep += currentStep; + } } - (BVAuthenticatedUser *)getBVAuthenticatedUser { - return self.bvUser; + return self.bvUser; } - (NSDictionary *)getCustomTargeting { - NSAssert(_apiKeyShopperAdvertising && 0 < _apiKeyShopperAdvertising.length, - @"You must supply apiKeyShopperAdvertising in the BVSDKManager."); - return [self.bvUser getTargetingKeywords]; + NSAssert(_apiKeyShopperAdvertising && 0 < _apiKeyShopperAdvertising.length, + @"You must supply apiKeyShopperAdvertising in the BVSDKManager."); + return [self.bvUser getTargetingKeywords]; } @end diff --git a/BVSDK/BVCommon/Private/BVNetworkingManager.m b/BVSDK/BVCommon/Private/BVNetworkingManager.m index 63fe343e..18cb7deb 100644 --- a/BVSDK/BVCommon/Private/BVNetworkingManager.m +++ b/BVSDK/BVCommon/Private/BVNetworkingManager.m @@ -40,10 +40,10 @@ - (nonnull NSURLSession *)bvNetworkingSession { } } - _bvNetworkingSession = + self->_bvNetworkingSession = [NSURLSession sessionWithConfiguration:config delegate:self - delegateQueue:_networkingOperationQueue]; + delegateQueue:self->_networkingOperationQueue]; }); return _bvNetworkingSession; diff --git a/BVSDK/BVConversations/Submission/Answer/BVAnswerSubmission.m b/BVSDK/BVConversations/Submission/Answer/BVAnswerSubmission.m index 47fc8d66..47a6d916 100644 --- a/BVSDK/BVConversations/Submission/Answer/BVAnswerSubmission.m +++ b/BVSDK/BVConversations/Submission/Answer/BVAnswerSubmission.m @@ -1,3 +1,6 @@ + + + // // AnswerSubmission.m // Conversations @@ -15,8 +18,8 @@ @interface BVAnswerSubmission () -@property(nonnull, readwrite) NSString *questionId; -@property(nonnull) NSString *answerText; +@property (nonnull, readwrite) NSString *questionId; +@property (nonnull) NSString *answerText; @property bool failureCalled; @end @@ -25,93 +28,96 @@ @implementation BVAnswerSubmission - (nonnull instancetype)initWithQuestionId:(nonnull NSString *)questionId answerText:(nonnull NSString *)answerText { - self = [super init]; - if (self) { - self.questionId = questionId; - self.answerText = answerText; - } - return self; + self = [super init]; + if (self) { + self.questionId = questionId; + self.answerText = answerText; + } + return self; } - (void)submit:(nonnull AnswerSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - if (self.action == BVSubmissionActionPreview) { - [[BVLogger sharedLogger] - warning:@"Submitting a 'BVAnswerSubmission' with action set to " - @"`BVSubmissionActionPreview` will not actially submit " - @"the answer! Set to `BVSubmissionActionSubmit` for real " - @"submission."]; - [self submitPreview:success failure:failure]; - } else { - [self submitPreview:^(BVAnswerSubmissionResponse *__nonnull response) { - [self submitForReal:success failure:failure]; + if (self.action == BVSubmissionActionPreview) { + [[BVLogger sharedLogger] + warning:@"Submitting a 'BVAnswerSubmission' with action set to " + @"`BVSubmissionActionPreview` will not actially submit " + @"the answer! Set to `BVSubmissionActionSubmit` for real " + @"submission."]; + [self submitPreview:success failure:failure]; + } else { + [self submitPreview:^(BVAnswerSubmissionResponse *__nonnull response) { + [self submitForReal:success failure:failure]; + } + failure:^(NSArray *__nonnull errors) { + [self sendErrors:errors failureCallback:failure]; + }]; } - failure:^(NSArray *__nonnull errors) { - [self sendErrors:errors failureCallback:failure]; - }]; - } } - (void)submitPreview:(AnswerSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - [self submitAnswerWithPhotoUrls:BVSubmissionActionPreview - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; -} - -- (void)submitForReal:(AnswerSubmissionCompletion)success - failure:(ConversationsFailureHandler)failure { - if ([self.photos count] == 0) { - [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit + [self submitAnswerWithPhotoUrls:BVSubmissionActionPreview photoUrls:@[] photoCaptions:@[] success:success failure:failure]; - return; - } - - // upload photos before submitting Answer - NSMutableArray *photoUrls = [NSMutableArray array]; - NSMutableArray *photoCaptions = [NSMutableArray array]; - - for (BVUploadablePhoto *photo in self.photos) { - [photo uploadForContentType:BVPhotoContentTypeAnswer - success:^(NSString *__nonnull photoUrl) { - - // Queue one event for each photo uploaded. - BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] - initWithProductId:self.questionId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNamePhoto - withAdditionalParams:@{@"detail1" : @"Answer"}]; - [BVPixel trackEvent:photoUploadEvent]; - - [photoUrls addObject:photoUrl]; - [photoCaptions addObject:photo.photoCaption]; - - // all photos uploaded! submit answer - if ([photoUrls count] == [self.photos count]) { - [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:photoUrls - photoCaptions:photoCaptions - success:success - failure:failure]; - } +} - } - failure:^(NSArray *__nonnull errors) { +- (void)submitForReal:(AnswerSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + if ([self.photos count] == 0) { + [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; + return; + } - if (!self.failureCalled) { - self.failureCalled = true; // only call failure block once, if - // multiple photos failed. - [self sendErrors:errors failureCallback:failure]; - } + // upload photos before submitting Answer + NSMutableArray *photoUrls = [NSMutableArray array]; + NSMutableArray *photoCaptions = [NSMutableArray array]; - }]; - } + for (BVUploadablePhoto *photo in self.photos) { + [photo uploadForContentType:BVPhotoContentTypeAnswer + success:^(NSString *__nonnull photoUrl) { + + // Queue one event for each photo uploaded. + BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.questionId + withBrand:nil + withProductType: + BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNamePhoto + withAdditionalParams:@{ + @"detail1" : @"Answer" + }]; + [BVPixel trackEvent:photoUploadEvent]; + + [photoUrls addObject:photoUrl]; + [photoCaptions addObject:photo.photoCaption]; + + // all photos uploaded! submit answer + if ([photoUrls count] == [self.photos count]) { + [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:photoUrls + photoCaptions:photoCaptions + success:success + failure:failure]; + } + + } + failure:^(NSArray *__nonnull errors) { + + if (!self.failureCalled) { + self.failureCalled = true; // only call failure block once, if + // multiple photos failed. + [self sendErrors:errors failureCallback:failure]; + } + + }]; + } } - (void)submitAnswerWithPhotoUrls:(BVSubmissionAction)action @@ -119,185 +125,193 @@ - (void)submitAnswerWithPhotoUrls:(BVSubmissionAction)action photoCaptions:(nonnull NSArray *)photoCaptions success:(nonnull AnswerSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - NSDictionary *parameters = [self createSubmissionParameters:action - photoUrls:photoUrls - photoCaptions:photoCaptions]; - NSData *postBody = [self transformToPostBody:parameters]; - - NSString *urlString = - [NSString stringWithFormat:@"%@submitanswer.json", - [BVConversationsRequest commonEndpoint]]; - NSURL *url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] - verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, - parameters]]; - - NSURLSession *session = nil; - id sessionDelegate = - [BVSDKManager sharedManager].urlSessionDelegate; - if (sessionDelegate && - [sessionDelegate respondsToSelector:@selector(URLSessionForBVObject:)]) { - session = [sessionDelegate URLSessionForBVObject:self]; - } - - session = session ?: [BVNetworkingManager sharedManager].bvNetworkingSession; - - NSURLSessionDataTask *postDataTask = [session - dataTaskWithRequest:request - completionHandler:^(NSData *data, NSURLResponse *response, - NSError *httpError) { - - @try { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) - response; // This dataTask is used with only HTTP requests. - NSInteger statusCode = httpResponse.statusCode; - NSError *jsonParsingError; - NSDictionary *json = - [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&jsonParsingError]; - BVAnswerSubmissionErrorResponse *errorResponse = - [[BVAnswerSubmissionErrorResponse alloc] - initWithApiResponse:json]; // fails gracefully - - [[BVLogger sharedLogger] - verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, - (long)statusCode]]; - - if (httpError) { - // network error was generated - [self sendError:httpError failureCallback:failure]; - } else if (statusCode >= 300) { - // HTTP status code indicates failure - NSError *statusError = - [NSError errorWithDomain:BVErrDomain - code:BV_ERROR_NETWORK_FAILED - userInfo:@{ - NSLocalizedDescriptionKey : - @"Photo upload failed." - }]; - [self sendError:statusError failureCallback:failure]; - } else if (jsonParsingError) { - // json parsing failed - [self sendError:jsonParsingError failureCallback:failure]; - } else if (errorResponse) { - // api returned successfully, but has bazaarvoice-specific - // errors. Example: 'invalid api key' - [self sendErrors:[errorResponse toNSErrors] - failureCallback:failure]; - } else { - // success! - - // Fire event now that we've confirmed the answer was - // successfully uploaded. - BVFeatureUsedEvent *answerQuestionEvent = [ - [BVFeatureUsedEvent alloc] - initWithProductId:_questionId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameAnswerQuestion - withAdditionalParams:nil]; + NSDictionary *parameters = [self createSubmissionParameters:action + photoUrls:photoUrls + photoCaptions:photoCaptions]; + NSData *postBody = [self transformToPostBody:parameters]; - [BVPixel trackEvent:answerQuestionEvent]; + NSString *urlString = + [NSString stringWithFormat:@"%@submitanswer.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; - BVAnswerSubmissionResponse *response = - [[BVAnswerSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(response); - }); - } + NSMutableURLRequest *request = + [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; - } @catch (NSException *exception) { - NSError *unknownError = - [NSError errorWithDomain:BVErrDomain - code:BV_ERROR_UNKNOWN - userInfo:@{ - NSLocalizedDescriptionKey : - @"An unknown parsing error occurred." - }]; - [self sendError:unknownError failureCallback:failure]; - } + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", + urlString, parameters]]; + + NSURLSession *session = nil; + id sessionDelegate = + [BVSDKManager sharedManager].urlSessionDelegate; + if (sessionDelegate && + [sessionDelegate + respondsToSelector:@selector(URLSessionForBVObject:)]) { + session = [sessionDelegate URLSessionForBVObject:self]; + } - }]; + session = + session ?: [BVNetworkingManager sharedManager].bvNetworkingSession; + + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + @try { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. + NSInteger statusCode = httpResponse.statusCode; + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + BVAnswerSubmissionErrorResponse *errorResponse = + [[BVAnswerSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", + json, (long)statusCode]]; + + if (httpError) { + // network error was generated + [self sendError:httpError failureCallback:failure]; + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Photo upload failed." + }]; + [self sendError:statusError failureCallback:failure]; + } else if (jsonParsingError) { + // json parsing failed + [self sendError:jsonParsingError failureCallback:failure]; + } else if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + [self sendErrors:[errorResponse toNSErrors] + failureCallback:failure]; + } else { + // success! + + // Fire event now that we've confirmed the answer was + // successfully uploaded. + BVFeatureUsedEvent *answerQuestionEvent = + [[BVFeatureUsedEvent alloc] + initWithProductId:self->_questionId + withBrand:nil + withProductType: + BVPixelProductTypeConversationsReviews + withEventName: + BVPixelFeatureUsedEventNameAnswerQuestion + withAdditionalParams:nil]; + + [BVPixel trackEvent:answerQuestionEvent]; + + BVAnswerSubmissionResponse *response = + [[BVAnswerSubmissionResponse alloc] + initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(response); + }); + } + + } @catch (NSException *exception) { + NSError *unknownError = [NSError + errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; + [self sendError:unknownError failureCallback:failure]; + } + + }]; - // start uploading answer - [postDataTask resume]; + // start uploading answer + [postDataTask resume]; - if (sessionDelegate && - [sessionDelegate respondsToSelector:@selector - (URLSessionTask:fromBVObject:withURLSession:)]) { - [sessionDelegate URLSessionTask:postDataTask - fromBVObject:self - withURLSession:session]; - } + if (sessionDelegate && + [sessionDelegate respondsToSelector:@selector(URLSessionTask: + fromBVObject: + withURLSession:)]) { + [sessionDelegate URLSessionTask:postDataTask + fromBVObject:self + withURLSession:session]; + } } - (nonnull NSDictionary *) createSubmissionParameters:(BVSubmissionAction)action photoUrls:(nonnull NSArray *)photoUrls photoCaptions:(nonnull NSArray *)photoCaptions { - NSMutableDictionary *parameters = - [NSMutableDictionary dictionaryWithDictionary:@{ - @"apiversion" : @"5.4", - @"answertext" : self.answerText, - @"questionid" : self.questionId, - }]; - - parameters[@"passkey"] = - [BVSDKManager sharedManager].configuration.apiKeyConversations; - parameters[@"action"] = [BVSubmissionActionUtil toString:action]; - - parameters[@"campaignid"] = self.campaignId; - parameters[@"locale"] = self.locale; - - parameters[@"hostedauthentication_authenticationemail"] = - self.hostedAuthenticationEmail; - parameters[@"hostedauthentication_callbackurl"] = - self.hostedAuthenticationCallback; - parameters[@"fp"] = self.fingerPrint; - parameters[@"user"] = self.user; - parameters[@"usernickname"] = self.userNickname; - parameters[@"useremail"] = self.userEmail; - parameters[@"userid"] = self.userId; - parameters[@"userlocation"] = self.userLocation; - - NSUInteger photoIndex = 0; - for (NSString *url in photoUrls) { - NSString *key = [NSString stringWithFormat:@"photourl_%i", (int)photoIndex]; - parameters[key] = url; - photoIndex += 1; - } - - NSUInteger captionIndex = 0; - for (NSString *caption in photoCaptions) { - NSString *key = - [NSString stringWithFormat:@"photocaption_%i", (int)captionIndex]; - parameters[key] = caption; - captionIndex += 1; - } - - if (self.sendEmailAlertWhenPublished) { - parameters[@"sendemailalertwhenpublished"] = - [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; - } - - if (self.agreedToTermsAndConditions) { - parameters[@"agreedtotermsandconditions"] = - [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; - } - - for (BVStringKeyValuePair *keyValuePair in self.customFormPairs) { - NSString *key = [keyValuePair key]; - NSString *value = [keyValuePair value]; - parameters[key] = value; - } - - return parameters; + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{ + @"apiversion" : @"5.4", + @"answertext" : self.answerText, + @"questionid" : self.questionId, + }]; + + parameters[@"passkey"] = + [BVSDKManager sharedManager].configuration.apiKeyConversations; + parameters[@"action"] = [BVSubmissionActionUtil toString:action]; + + parameters[@"campaignid"] = self.campaignId; + parameters[@"locale"] = self.locale; + + parameters[@"hostedauthentication_authenticationemail"] = + self.hostedAuthenticationEmail; + parameters[@"hostedauthentication_callbackurl"] = + self.hostedAuthenticationCallback; + parameters[@"fp"] = self.fingerPrint; + parameters[@"user"] = self.user; + parameters[@"usernickname"] = self.userNickname; + parameters[@"useremail"] = self.userEmail; + parameters[@"userid"] = self.userId; + parameters[@"userlocation"] = self.userLocation; + + NSUInteger photoIndex = 0; + for (NSString *url in photoUrls) { + NSString *key = + [NSString stringWithFormat:@"photourl_%i", (int)photoIndex]; + parameters[key] = url; + photoIndex += 1; + } + + NSUInteger captionIndex = 0; + for (NSString *caption in photoCaptions) { + NSString *key = + [NSString stringWithFormat:@"photocaption_%i", (int)captionIndex]; + parameters[key] = caption; + captionIndex += 1; + } + + if (self.sendEmailAlertWhenPublished) { + parameters[@"sendemailalertwhenpublished"] = + [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; + } + + if (self.agreedToTermsAndConditions) { + parameters[@"agreedtotermsandconditions"] = + [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; + } + + for (BVStringKeyValuePair *keyValuePair in self.customFormPairs) { + NSString *key = [keyValuePair key]; + NSString *value = [keyValuePair value]; + parameters[key] = value; + } + + return parameters; } @end diff --git a/BVSDK/BVCurationsUI/BVCurationsPostViewController.m b/BVSDK/BVCurationsUI/BVCurationsPostViewController.m index 88f6a8b3..b96865af 100644 --- a/BVSDK/BVCurationsUI/BVCurationsPostViewController.m +++ b/BVSDK/BVCurationsUI/BVCurationsPostViewController.m @@ -14,10 +14,10 @@ @interface BVCurationsPostViewController () -@property(nonatomic, strong) BVCurationsAddPostRequest *postRequest; -@property(nonatomic, strong) UIImage *logo; -@property(nonatomic, strong) UIColor *navBarColor; -@property(nonatomic, strong) UIColor *navBarTintColor; +@property (nonatomic, strong) BVCurationsAddPostRequest *postRequest; +@property (nonatomic, strong) UIImage *logo; +@property (nonatomic, strong) UIColor *navBarColor; +@property (nonatomic, strong) UIColor *navBarTintColor; @end @@ -28,177 +28,180 @@ - (nonnull instancetype)initWithPostRequest: logoImage:(nonnull UIImage *)logo bavBarColor:(nonnull UIColor *)navBarColor navBarTintColor:(nonnull UIColor *)navBarTintColor { - if ((self = [super init])) { - _postRequest = postRequest; - _logo = logo; - _navBarColor = navBarColor; - _navBarTintColor = navBarTintColor; - } - return self; + if ((self = [super init])) { + _postRequest = postRequest; + _logo = logo; + _navBarColor = navBarColor; + _navBarTintColor = navBarTintColor; + } + return self; } - (void)viewDidLoad { - [super viewDidLoad]; - [self setupNavBar]; - self.textView.text = _postRequest.text; - [self validateContent]; - - BVInViewEvent *inViewEvent = - [[BVInViewEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeCurations - withContainerId:@"CurationsSubmissionController" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inViewEvent]; + [super viewDidLoad]; + [self setupNavBar]; + self.textView.text = _postRequest.text; + [self validateContent]; + + BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeCurations + withContainerId:@"CurationsSubmissionController" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inViewEvent]; } - (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. } - (void)setupNavBar { - self.navigationController.navigationBar.tintColor = _navBarTintColor; - - CGFloat padding = 16; - CGSize size = self.navigationController.navigationBar.frame.size; - size.width -= padding * 2; - UIImage *bkgd = - [self navBarBackgroundImage:_navBarColor logo:_logo size:size]; - [self.navigationController.navigationBar - setBackgroundImage:bkgd - forBarMetrics:UIBarMetricsDefault]; + self.navigationController.navigationBar.tintColor = _navBarTintColor; + + CGFloat padding = 16; + CGSize size = self.navigationController.navigationBar.frame.size; + size.width -= padding * 2; + UIImage *bkgd = + [self navBarBackgroundImage:_navBarColor logo:_logo size:size]; + [self.navigationController.navigationBar + setBackgroundImage:bkgd + forBarMetrics:UIBarMetricsDefault]; } - (void)didSelectCancel { - [super didSelectCancel]; - [self dismiss]; + [super didSelectCancel]; + [self dismiss]; } - (void)didSelectPost { - [super didSelectPost]; - _postRequest.text = self.textView.text; - - if (_didBeginPost) { - _didBeginPost(); - } - - BVCurationsPhotoUploader *uploader = [[BVCurationsPhotoUploader alloc] init]; - [uploader submitCurationsContentWithParams:_postRequest - completionHandler:^{ - [self completePost:nil]; - } - withFailure:^(NSError *error) { - [self completePost:error]; - }]; + [super didSelectPost]; + _postRequest.text = self.textView.text; + + if (_didBeginPost) { + _didBeginPost(); + } + + BVCurationsPhotoUploader *uploader = + [[BVCurationsPhotoUploader alloc] init]; + [uploader submitCurationsContentWithParams:_postRequest + completionHandler:^{ + [self completePost:nil]; + } + withFailure:^(NSError *error) { + [self completePost:error]; + }]; } - (void)completePost:(NSError *)error { - if (!error) { - BVFeatureUsedEvent *submittedPostEvent = [[BVFeatureUsedEvent alloc] - initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventNameWriteReview - withAdditionalParams:nil]; - - [BVPixel trackEvent:submittedPostEvent]; - } - - [self dismissViewControllerAnimated:NO - completion:^{ - if (_didCompletePost) { - _didCompletePost(error); - } - }]; + if (!error) { + BVFeatureUsedEvent *submittedPostEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventNameWriteReview + withAdditionalParams:nil]; + + [BVPixel trackEvent:submittedPostEvent]; + } + + [self dismissViewControllerAnimated:NO + completion:^{ + if (self->_didCompletePost) { + self->_didCompletePost(error); + } + }]; } - (BOOL)isContentValid { - return self.textView.text.length && _postRequest.alias.length && - _postRequest.token.length; + return self.textView.text.length && _postRequest.alias.length && + _postRequest.token.length; } - (UIView *)loadPreviewView { - UIImageView *imageView = - [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; - imageView.contentMode = UIViewContentModeScaleAspectFit; - imageView.image = [self proportionallyScaledImageOf:_postRequest.image - inBoundingDimensions:imageView.frame.size]; + UIImageView *imageView = + [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.image = [self proportionallyScaledImageOf:_postRequest.image + inBoundingDimensions:imageView.frame.size]; - return imageView; + return imageView; } - (UIImage *)navBarBackgroundImage:(UIColor *)backgroundColor logo:(UIImage *)logo size:(CGSize)size { - CGRect frame = CGRectZero; - frame.size = size; - UIGraphicsBeginImageContextWithOptions(size, false, 0); - [backgroundColor setFill]; - UIRectFill(frame); - - CGFloat logoPadding = 2; - CGFloat dimension = size.height - logoPadding; - CGSize logoSize = - [self proportionalSizeOf:logo.size - inBoundingSize:CGSizeMake(CGFLOAT_MAX, dimension)]; - CGRect logoFrame = - CGRectMake(size.width / 2 - logoSize.width / 2, logoPadding / 2, - logoSize.width, logoSize.height); - [logo drawInRect:logoFrame]; - - UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return backgroundImage; + CGRect frame = CGRectZero; + frame.size = size; + UIGraphicsBeginImageContextWithOptions(size, false, 0); + [backgroundColor setFill]; + UIRectFill(frame); + + CGFloat logoPadding = 2; + CGFloat dimension = size.height - logoPadding; + CGSize logoSize = + [self proportionalSizeOf:logo.size + inBoundingSize:CGSizeMake(CGFLOAT_MAX, dimension)]; + CGRect logoFrame = + CGRectMake(size.width / 2 - logoSize.width / 2, logoPadding / 2, + logoSize.width, logoSize.height); + [logo drawInRect:logoFrame]; + + UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return backgroundImage; } - (NSArray *)configurationItems { - SLComposeSheetConfigurationItem *item = [SLComposeSheetConfigurationItem new]; - item.title = @"Username"; - item.value = _postRequest.alias; + SLComposeSheetConfigurationItem *item = + [SLComposeSheetConfigurationItem new]; + item.title = @"Username"; + item.value = _postRequest.alias; - return @[ item ]; + return @[ item ]; } - (UIImage *)proportionallyScaledImageOf:(UIImage *)image inBoundingDimensions:(CGSize)dimensions { - CGSize imgSize = image.size; - CGSize newSize = [self proportionalSizeOf:imgSize inBoundingSize:dimensions]; - - CGImageRef cgImage = image.CGImage; - size_t bpc = CGImageGetBitsPerComponent(cgImage); - size_t bpr = CGImageGetBytesPerRow(cgImage); - CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); - CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage); - - CGContextRef context = CGBitmapContextCreate( - nil, newSize.width, newSize.height, bpc, bpr, colorSpace, bitmapInfo); - CGContextSetInterpolationQuality(context, kCGInterpolationHigh); - - CGContextDrawImage(context, CGRectMake(0, 0, newSize.width, newSize.height), - cgImage); - return [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)]; + CGSize imgSize = image.size; + CGSize newSize = + [self proportionalSizeOf:imgSize inBoundingSize:dimensions]; + + CGImageRef cgImage = image.CGImage; + size_t bpc = CGImageGetBitsPerComponent(cgImage); + size_t bpr = CGImageGetBytesPerRow(cgImage); + CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage); + + CGContextRef context = CGBitmapContextCreate( + nil, newSize.width, newSize.height, bpc, bpr, colorSpace, bitmapInfo); + CGContextSetInterpolationQuality(context, kCGInterpolationHigh); + + CGContextDrawImage(context, CGRectMake(0, 0, newSize.width, newSize.height), + cgImage); + return [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)]; } - (CGSize)proportionalSizeOf:(CGSize)size inBoundingSize:(CGSize)boundingSize { - CGFloat newW = boundingSize.width; - CGFloat newH = newW * size.height / size.width; - - if (newH > boundingSize.height) { - newH = boundingSize.height; - newW = newH * size.width / size.height; - } - return CGSizeMake(newW, newH); + CGFloat newW = boundingSize.width; + CGFloat newH = newW * size.height / size.width; + + if (newH > boundingSize.height) { + newH = boundingSize.height; + newW = newH * size.width / size.height; + } + return CGSizeMake(newW, newH); } - (void)dismiss { - [self dismissViewControllerAnimated:YES - completion:^{ - if (_didPressCancel) { - _didPressCancel(); - } - }]; + [self dismissViewControllerAnimated:YES + completion:^{ + if (self->_didPressCancel) { + self->_didPressCancel(); + } + }]; } @end diff --git a/BVSDK/BVCurationsUI/BVCurationsUICollectionView.m b/BVSDK/BVCurationsUI/BVCurationsUICollectionView.m index 605fb918..55057885 100644 --- a/BVSDK/BVCurationsUI/BVCurationsUICollectionView.m +++ b/BVSDK/BVCurationsUI/BVCurationsUICollectionView.m @@ -1,3 +1,6 @@ + + + // // BVCurationsCollectionView.m // Bazaarvoice SDK @@ -13,17 +16,17 @@ @interface BVCurationsUICollectionView () < UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UIScrollViewDelegate> -@property(nonatomic, strong) BVCurationsFeedLoader *curationsFeedLoader; -@property(nonatomic, strong) +@property (nonatomic, strong) BVCurationsFeedLoader *curationsFeedLoader; +@property (nonatomic, strong) NSMutableArray *curationsFeedItems; -@property(nonatomic, strong) NSNumber *lastFetchedTimeStamp; -@property(nonatomic, assign) CGFloat totalHeight; -@property(nonatomic, assign) BOOL pendingUpdate; -@property(nonatomic, assign) BOOL allItemsFetched; -@property(nonatomic, strong) NSMutableSet *impressedItems; -@property(nonatomic, assign) CGFloat oldTimeStamp; -@property(nonatomic, assign) CGFloat oldBounds; -@property(nonatomic, strong) NSNumber *shouldRequestImageLoad; +@property (nonatomic, strong) NSNumber *lastFetchedTimeStamp; +@property (nonatomic, assign) CGFloat totalHeight; +@property (nonatomic, assign) BOOL pendingUpdate; +@property (nonatomic, assign) BOOL allItemsFetched; +@property (nonatomic, strong) NSMutableSet *impressedItems; +@property (nonatomic, assign) CGFloat oldTimeStamp; +@property (nonatomic, assign) CGFloat oldBounds; +@property (nonatomic, strong) NSNumber *shouldRequestImageLoad; @end #define BVCurationCVCellID @"BVCurationsCollectionViewCell" @@ -35,357 +38,363 @@ @implementation BVCurationsUICollectionView - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - if ([super initWithFrame:frame collectionViewLayout:layout]) { - [self setup]; - } - return self; + if ([super initWithFrame:frame collectionViewLayout:layout]) { + [self setup]; + } + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - if ([super initWithCoder:aDecoder]) { - [self setup]; - } + if ([super initWithCoder:aDecoder]) { + [self setup]; + } - return self; + return self; } - (void)setup { - _curationsFeedLoader = [[BVCurationsFeedLoader alloc] init]; - _curationsFeedItems = [NSMutableArray new]; - _impressedItems = [NSMutableSet new]; - _fetchSize = 10; - _infiniteScrollEnabled = YES; - _itemsPerRow = 2; - _pendingUpdate = NO; - _shouldRequestImageLoad = @YES; - - self.delegate = self; - self.dataSource = self; - [self registerClass:[BVCurationsUICollectionViewCell class] - forCellWithReuseIdentifier:BVCurationCVCellID]; - UICollectionViewFlowLayout *layout = - (UICollectionViewFlowLayout *)self.collectionViewLayout; - layout.minimumLineSpacing = BVCurationsDesiredPadding; - layout.minimumInteritemSpacing = BVCurationsDesiredPadding; + _curationsFeedLoader = [[BVCurationsFeedLoader alloc] init]; + _curationsFeedItems = [NSMutableArray new]; + _impressedItems = [NSMutableSet new]; + _fetchSize = 10; + _infiniteScrollEnabled = YES; + _itemsPerRow = 2; + _pendingUpdate = NO; + _shouldRequestImageLoad = @YES; + + self.delegate = self; + self.dataSource = self; + [self registerClass:[BVCurationsUICollectionViewCell class] + forCellWithReuseIdentifier:BVCurationCVCellID]; + UICollectionViewFlowLayout *layout = + (UICollectionViewFlowLayout *)self.collectionViewLayout; + layout.minimumLineSpacing = BVCurationsDesiredPadding; + layout.minimumInteritemSpacing = BVCurationsDesiredPadding; } - (void)loadFeed { - NSUInteger fetchSize = _fetchSize; - - /* - If enabled we need to bump the initial fetch size to load just enough - items to enable scrolling (need a little bit off screen) - */ - if (_infiniteScrollEnabled) { - CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; - NSUInteger totalExpected = _curationsFeedItems.count + fetchSize; - CGFloat estimatedTotalHeight = - oneCellHeight * ceil(totalExpected / _itemsPerRow); - if (estimatedTotalHeight < self.bounds.size.height) { - CGFloat diff = self.bounds.size.height - estimatedTotalHeight; - NSUInteger numRowOffSet = ceil(diff / oneCellHeight); - fetchSize += (numRowOffSet * _itemsPerRow); + NSUInteger fetchSize = _fetchSize; + + /* + If enabled we need to bump the initial fetch size to load just enough + items to enable scrolling (need a little bit off screen) + */ + if (_infiniteScrollEnabled) { + CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; + NSUInteger totalExpected = _curationsFeedItems.count + fetchSize; + CGFloat estimatedTotalHeight = + oneCellHeight * ceil(totalExpected / _itemsPerRow); + if (estimatedTotalHeight < self.bounds.size.height) { + CGFloat diff = self.bounds.size.height - estimatedTotalHeight; + NSUInteger numRowOffSet = ceil(diff / oneCellHeight); + fetchSize += (numRowOffSet * _itemsPerRow); + } } - } - [self loadCurationsFeed:fetchSize lastFetched:_lastFetchedTimeStamp]; + [self loadCurationsFeed:fetchSize lastFetched:_lastFetchedTimeStamp]; } - (void)setDelegate:(id)delegate { - // placeholder may want to disallow - super.delegate = delegate; + // placeholder may want to disallow + super.delegate = delegate; } - (void)setDataSource:(id)dataSource { - // placeholder may want to disallow - super.dataSource = dataSource; + // placeholder may want to disallow + super.dataSource = dataSource; } - (void)setBvCurationsUILayout:(BVCurationsUILayout)bvCurationsUILayout { - _bvCurationsUILayout = bvCurationsUILayout; - UICollectionViewFlowLayout *layout = - (UICollectionViewFlowLayout *)self.collectionViewLayout; - switch (_bvCurationsUILayout) { - case BVCurationsUILayoutGrid: - layout.scrollDirection = UICollectionViewScrollDirectionVertical; - break; - case BVCurationsUILayoutCarousel: - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; - break; - default: - break; - } - [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count - itemsPerRow:self.itemsPerRow]; - - [self reloadData]; + _bvCurationsUILayout = bvCurationsUILayout; + UICollectionViewFlowLayout *layout = + (UICollectionViewFlowLayout *)self.collectionViewLayout; + switch (_bvCurationsUILayout) { + case BVCurationsUILayoutGrid: + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + break; + case BVCurationsUILayoutCarousel: + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + break; + default: + break; + } + [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count + itemsPerRow:self.itemsPerRow]; + + [self reloadData]; } - (NSArray *)feedItems { - return [NSArray arrayWithArray:_curationsFeedItems]; + return [NSArray arrayWithArray:_curationsFeedItems]; } - (void)loadCurationsFeed:(NSUInteger)limit lastFetched:(NSNumber *)lastFetchedTimeStamp { - if (_pendingUpdate || _allItemsFetched) { - return; - } - - if (!_groups) { - [[BVLogger sharedLogger] error:@"Groups not set on " - @"BVCurationsUICollectionView. Unable " - @"to load feed"]; - return; - } - - _pendingUpdate = YES; - BVCurationsFeedRequest *req = - [[BVCurationsFeedRequest alloc] initWithGroups:_groups]; - req.limit = limit; - req.hasPhotoOrVideo = @YES; - if (_lastFetchedTimeStamp) { - req.before = _lastFetchedTimeStamp; - } - - if (_productId && 0 < _productId.length) { - req.externalId = _productId; - } - - __weak typeof(self) weakSelf = self; - [_curationsFeedLoader loadFeedWithRequest:req - completionHandler:^(NSArray *items) { - - BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] - initWithProductId:req.externalId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withContainerId:@"CurationsUICollectionView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inViewEvent]; - - weakSelf.allItemsFetched = !items.count; - weakSelf.pendingUpdate = NO; - weakSelf.lastFetchedTimeStamp = items.lastObject.timestamp; - [weakSelf.curationsFeedItems addObjectsFromArray:items]; - [weakSelf - updateInfiniteScrollMeasurements:weakSelf.curationsFeedItems.count - itemsPerRow:weakSelf.itemsPerRow]; - - BOOL errorState = items.count > limit; // if for whatever reason the - // api returns more items than - // expected we'll just reload - if (errorState) { - [self reloadData]; - } else { - NSMutableArray *indexPaths = [NSMutableArray new]; - NSUInteger inserted = (limit <= items.count) ? limit : items.count; - for (NSUInteger i = 0; i < inserted; i++) { - [indexPaths - addObject:[NSIndexPath - indexPathForItem:weakSelf.curationsFeedItems - .count - - 1 - i - inSection:0]]; - } + if (_pendingUpdate || _allItemsFetched) { + return; + } - [self insertItemsAtIndexPaths:indexPaths]; - } - } - withFailure:^(NSError *err) { - weakSelf.pendingUpdate = NO; - if ([_curationsDelegate - respondsToSelector:@selector(curationsFailedToLoadFeed:)]) { - [_curationsDelegate curationsFailedToLoadFeed:err]; + if (!_groups) { + [[BVLogger sharedLogger] error:@"Groups not set on " + @"BVCurationsUICollectionView. Unable " + @"to load feed"]; + return; + } + + _pendingUpdate = YES; + BVCurationsFeedRequest *req = + [[BVCurationsFeedRequest alloc] initWithGroups:_groups]; + req.limit = limit; + req.hasPhotoOrVideo = @YES; + if (_lastFetchedTimeStamp) { + req.before = _lastFetchedTimeStamp; + } + + if (_productId && 0 < _productId.length) { + req.externalId = _productId; + } + + __weak typeof(self) weakSelf = self; + [_curationsFeedLoader loadFeedWithRequest:req + completionHandler:^(NSArray *items) { + + BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] + initWithProductId:req.externalId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withContainerId:@"CurationsUICollectionView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inViewEvent]; + + weakSelf.allItemsFetched = !items.count; + weakSelf.pendingUpdate = NO; + weakSelf.lastFetchedTimeStamp = items.lastObject.timestamp; + [weakSelf.curationsFeedItems addObjectsFromArray:items]; + [weakSelf + updateInfiniteScrollMeasurements:weakSelf.curationsFeedItems.count + itemsPerRow:weakSelf.itemsPerRow]; + + BOOL errorState = items.count > limit; // if for whatever reason the + // api returns more items than + // expected we'll just reload + if (errorState) { + [self reloadData]; + } else { + NSMutableArray *indexPaths = [NSMutableArray new]; + NSUInteger inserted = + (limit <= items.count) ? limit : items.count; + for (NSUInteger i = 0; i < inserted; i++) { + [indexPaths + addObject:[NSIndexPath + indexPathForItem:weakSelf.curationsFeedItems + .count - + 1 - i + inSection:0]]; + } + + [self insertItemsAtIndexPaths:indexPaths]; + } } - }]; + withFailure:^(NSError *err) { + weakSelf.pendingUpdate = NO; + if ([self->_curationsDelegate + respondsToSelector:@selector(curationsFailedToLoadFeed:)]) { + [self->_curationsDelegate curationsFailedToLoadFeed:err]; + } + }]; } - (void)setItemsPerRow:(NSUInteger)itemsPerRow { - _itemsPerRow = itemsPerRow; - [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count - itemsPerRow:self.itemsPerRow]; - [self loadFeed]; + _itemsPerRow = itemsPerRow; + [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count + itemsPerRow:self.itemsPerRow]; + [self loadFeed]; } - (void)updateInfiniteScrollMeasurements:(NSInteger)itemCount itemsPerRow:(NSInteger)itemsPerRow { - _totalHeight = [self getTotalHeight:itemCount itemsPerRow:itemsPerRow]; + _totalHeight = [self getTotalHeight:itemCount itemsPerRow:itemsPerRow]; } - (CGFloat)getTotalHeight:(NSInteger)itemCount itemsPerRow:(NSInteger)itemsPerRow { - CGFloat oneCellHeight = [self getOneCellDim:itemsPerRow]; - NSInteger numRows; - if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { - numRows = ceil(_curationsFeedItems.count / _itemsPerRow); - } else { - numRows = _curationsFeedItems.count; - } - return oneCellHeight * numRows; + CGFloat oneCellHeight = [self getOneCellDim:itemsPerRow]; + NSInteger numRows; + if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { + numRows = ceil(_curationsFeedItems.count / _itemsPerRow); + } else { + numRows = _curationsFeedItems.count; + } + return oneCellHeight * numRows; } - (CGFloat)getOneCellDim:(NSInteger)itemsPerRow { - CGFloat dim; - if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { - dim = (self.frame.size.width / itemsPerRow) - BVCurationsDesiredPadding; - } else { - dim = self.frame.size.height - BVCurationsDesiredPadding; - } - return dim; + CGFloat dim; + if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { + dim = (self.frame.size.width / itemsPerRow) - BVCurationsDesiredPadding; + } else { + dim = self.frame.size.height - BVCurationsDesiredPadding; + } + return dim; } - (void)setFrame:(CGRect)frame { - super.frame = frame; - if (_itemsPerRow) { - [self updateInfiniteScrollMeasurements:_curationsFeedItems.count - itemsPerRow:_itemsPerRow]; - } + super.frame = frame; + if (_itemsPerRow) { + [self updateInfiniteScrollMeasurements:_curationsFeedItems.count + itemsPerRow:_itemsPerRow]; + } } #pragma mark : UICollectionViewDataSource - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return _curationsFeedItems.count; + return _curationsFeedItems.count; } - (__kindof UICollectionViewCell *)collectionView: (UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - BVCurationsUICollectionViewCell *cell = - [collectionView dequeueReusableCellWithReuseIdentifier:BVCurationCVCellID - forIndexPath:indexPath]; - cell.shouldLoadObject = self; - cell.shouldLoadKeypath = @"shouldRequestImageLoad"; - cell.loadImageHandler = - ^(NSString *__nonnull imageUrl, - BVCurationsLoadImageCompletion __nonnull completion) { - [_curationsDelegate curationsLoadImage:imageUrl completion:completion]; - }; - cell.loadImageIsCachedHandler = ^( - NSString *__nonnull imageUrl, - BVCurationsIsImageCachedCompletion completion) { - [_curationsDelegate - curationsImageIsCached:imageUrl - completion:^(BOOL isCached, NSString *__nonnull imageUrl) { - completion(isCached, imageUrl); - }]; - }; - cell.curationsFeedItem = _curationsFeedItems[indexPath.item]; - - if (![_impressedItems containsObject:cell.curationsFeedItem]) { - [_impressedItems addObject:cell.curationsFeedItem]; - - BVCurationsFeedItem *item = cell.curationsFeedItem; - - BVImpressionEvent *impression = [[BVImpressionEvent alloc] - initWithProductId:item.externalId - withContentId:item.contentId - withCategoryId:nil - withProductType:BVPixelProductTypeCurations - withContentType:BVPixelImpressionContentCurationsFeedItem - withBrand:nil - withAdditionalParams:@{@"syndicationSource" : item.sourceClient}]; + BVCurationsUICollectionViewCell *cell = [collectionView + dequeueReusableCellWithReuseIdentifier:BVCurationCVCellID + forIndexPath:indexPath]; + cell.shouldLoadObject = self; + cell.shouldLoadKeypath = @"shouldRequestImageLoad"; + cell.loadImageHandler = + ^(NSString *__nonnull imageUrl, + BVCurationsLoadImageCompletion __nonnull completion) { + [self->_curationsDelegate curationsLoadImage:imageUrl + completion:completion]; + }; + cell.loadImageIsCachedHandler = + ^(NSString *__nonnull imageUrl, + BVCurationsIsImageCachedCompletion completion) { + [self->_curationsDelegate + curationsImageIsCached:imageUrl + completion:^(BOOL isCached, + NSString *__nonnull imageUrl) { + completion(isCached, imageUrl); + }]; + }; + cell.curationsFeedItem = _curationsFeedItems[indexPath.item]; + + if (![_impressedItems containsObject:cell.curationsFeedItem]) { + [_impressedItems addObject:cell.curationsFeedItem]; + + BVCurationsFeedItem *item = cell.curationsFeedItem; + + BVImpressionEvent *impression = [[BVImpressionEvent alloc] + initWithProductId:item.externalId + withContentId:item.contentId + withCategoryId:nil + withProductType:BVPixelProductTypeCurations + withContentType:BVPixelImpressionContentCurationsFeedItem + withBrand:nil + withAdditionalParams:@{ + @"syndicationSource" : item.sourceClient + }]; - [BVPixel trackEvent:impression]; - } - return cell; + [BVPixel trackEvent:impression]; + } + return cell; } #pragma mark : UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - CGFloat oneCellDim = [self getOneCellDim:_itemsPerRow]; - return CGSizeMake(oneCellDim, oneCellDim); + CGFloat oneCellDim = [self getOneCellDim:_itemsPerRow]; + return CGSizeMake(oneCellDim, oneCellDim); } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { - CGFloat padding = BVCurationsDesiredPadding / 2; - return UIEdgeInsetsMake(padding, padding, padding, padding); + CGFloat padding = BVCurationsDesiredPadding / 2; + return UIEdgeInsetsMake(padding, padding, padding, padding); } #pragma mark : UICollectionViewDelegate - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - [collectionView deselectItemAtIndexPath:indexPath animated:YES]; - BVCurationsFeedItem *item = _curationsFeedItems[indexPath.item]; - - BVFeatureUsedEvent *tapEvent = [[BVFeatureUsedEvent alloc] - initWithProductId:item.externalId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventContentClick - withAdditionalParams:nil]; - - [BVPixel trackEvent:tapEvent]; - - if ([_curationsDelegate - respondsToSelector:@selector(curationsDidSelectFeedItem:)]) { - [_curationsDelegate curationsDidSelectFeedItem:item]; - } + [collectionView deselectItemAtIndexPath:indexPath animated:YES]; + BVCurationsFeedItem *item = _curationsFeedItems[indexPath.item]; + + BVFeatureUsedEvent *tapEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:item.externalId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventContentClick + withAdditionalParams:nil]; + + [BVPixel trackEvent:tapEvent]; + + if ([_curationsDelegate + respondsToSelector:@selector(curationsDidSelectFeedItem:)]) { + [_curationsDelegate curationsDidSelectFeedItem:item]; + } } #pragma mark : UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - if (!_infiniteScrollEnabled || _allItemsFetched) { - return; - } - - CGFloat timestamp = [[NSDate date] timeIntervalSince1970]; - CGFloat newBounds = (_bvCurationsUILayout == BVCurationsUILayoutGrid) - ? scrollView.bounds.origin.y - : scrollView.bounds.origin.x; - CGFloat boundsDiff = fabs(_oldBounds - newBounds); - CGFloat timeDiff = timestamp - _oldTimeStamp; - _oldTimeStamp = timestamp; - _oldBounds = newBounds; - CGFloat velocity = boundsDiff / timeDiff; - - if (velocity < BVCurationsVelocityThreshold) { - [self attemptToTransitionToLoadImages]; - } else { - _shouldRequestImageLoad = @NO; - } - - CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; - CGFloat lowerBounds; - if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { - lowerBounds = scrollView.bounds.origin.y + scrollView.bounds.size.height; - } else { - lowerBounds = scrollView.bounds.origin.x + scrollView.bounds.size.width; - } - - CGFloat totalHeigthWithReloadOffest = - (_totalHeight - (oneCellHeight * BVCurationsRowsOffScreenBeforeReload)); - if (lowerBounds > totalHeigthWithReloadOffest) { - [self loadCurationsFeed:_fetchSize lastFetched:_lastFetchedTimeStamp]; - } + if (!_infiniteScrollEnabled || _allItemsFetched) { + return; + } + + CGFloat timestamp = [[NSDate date] timeIntervalSince1970]; + CGFloat newBounds = (_bvCurationsUILayout == BVCurationsUILayoutGrid) + ? scrollView.bounds.origin.y + : scrollView.bounds.origin.x; + CGFloat boundsDiff = fabs(_oldBounds - newBounds); + CGFloat timeDiff = timestamp - _oldTimeStamp; + _oldTimeStamp = timestamp; + _oldBounds = newBounds; + CGFloat velocity = boundsDiff / timeDiff; + + if (velocity < BVCurationsVelocityThreshold) { + [self attemptToTransitionToLoadImages]; + } else { + _shouldRequestImageLoad = @NO; + } + + CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; + CGFloat lowerBounds; + if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { + lowerBounds = + scrollView.bounds.origin.y + scrollView.bounds.size.height; + } else { + lowerBounds = scrollView.bounds.origin.x + scrollView.bounds.size.width; + } + + CGFloat totalHeigthWithReloadOffest = + (_totalHeight - (oneCellHeight * BVCurationsRowsOffScreenBeforeReload)); + if (lowerBounds > totalHeigthWithReloadOffest) { + [self loadCurationsFeed:_fetchSize lastFetched:_lastFetchedTimeStamp]; + } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - [self attemptToTransitionToLoadImages]; + [self attemptToTransitionToLoadImages]; - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] - initWithProductId:_productId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:_productId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; - [BVPixel trackEvent:scrollEvent]; + [BVPixel trackEvent:scrollEvent]; } - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { - [self attemptToTransitionToLoadImages]; + [self attemptToTransitionToLoadImages]; } - (void)attemptToTransitionToLoadImages { - BOOL transition = !_shouldRequestImageLoad.boolValue; - if (transition) { - self.shouldRequestImageLoad = @YES; - } + BOOL transition = !_shouldRequestImageLoad.boolValue; + if (transition) { + self.shouldRequestImageLoad = @YES; + } } @end diff --git a/BVSDK/BVExtensionNotifications/BVProductReviewNotificationViewController.m b/BVSDK/BVExtensionNotifications/BVProductReviewNotificationViewController.m index 5ba62351..4e0a9535 100644 --- a/BVSDK/BVExtensionNotifications/BVProductReviewNotificationViewController.m +++ b/BVSDK/BVExtensionNotifications/BVProductReviewNotificationViewController.m @@ -11,125 +11,125 @@ @interface BVProductReviewNotificationViewController () -@property(nonatomic, strong) UIImageView *productImageView; -@property(nonatomic, strong) UILabel *productNameLbl; +@property (nonatomic, strong) UIImageView *productImageView; +@property (nonatomic, strong) UILabel *productNameLbl; @end @implementation BVProductReviewNotificationViewController - (void)viewDidLoad { - [super viewDidLoad]; - _productNameLbl = [[UILabel alloc] init]; - _productNameLbl.translatesAutoresizingMaskIntoConstraints = NO; - _productNameLbl.font = [UIFont systemFontOfSize:18.0]; - _productNameLbl.textAlignment = NSTextAlignmentCenter; - [self.view addSubview:_productNameLbl]; + [super viewDidLoad]; + _productNameLbl = [[UILabel alloc] init]; + _productNameLbl.translatesAutoresizingMaskIntoConstraints = NO; + _productNameLbl.font = [UIFont systemFontOfSize:18.0]; + _productNameLbl.textAlignment = NSTextAlignmentCenter; + [self.view addSubview:_productNameLbl]; - [self.view addConstraint:[NSLayoutConstraint - constraintWithItem:_productNameLbl - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeTopMargin - multiplier:1.0f - constant:0]]; - [self.view addConstraint:[NSLayoutConstraint - constraintWithItem:_productNameLbl - attribute:NSLayoutAttributeLeft - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeLeftMargin - multiplier:1 - constant:0]]; - [self.view addConstraint:[NSLayoutConstraint - constraintWithItem:_productNameLbl - attribute:NSLayoutAttributeRight - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeRightMargin - multiplier:1 - constant:0]]; - [_productNameLbl - addConstraint:[NSLayoutConstraint - constraintWithItem:_productNameLbl - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0f - constant:25]]; - [_productNameLbl - setContentCompressionResistancePriority:UILayoutPriorityRequired - forAxis:UILayoutConstraintAxisVertical]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTopMargin + multiplier:1.0f + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeftMargin + multiplier:1 + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeRight + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeRightMargin + multiplier:1 + constant:0]]; + [_productNameLbl + addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0f + constant:25]]; + [_productNameLbl + setContentCompressionResistancePriority:UILayoutPriorityRequired + forAxis:UILayoutConstraintAxisVertical]; - _productImageView = [[UIImageView alloc] init]; - _productImageView.translatesAutoresizingMaskIntoConstraints = NO; - _productImageView.contentMode = UIViewContentModeScaleAspectFit; - [self.view addSubview:_productImageView]; + _productImageView = [[UIImageView alloc] init]; + _productImageView.translatesAutoresizingMaskIntoConstraints = NO; + _productImageView.contentMode = UIViewContentModeScaleAspectFit; + [self.view addSubview:_productImageView]; - [self.view addConstraint:[NSLayoutConstraint - constraintWithItem:_productNameLbl - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:_productImageView - attribute:NSLayoutAttributeTop - multiplier:1 - constant:0]]; - [self.view - addConstraint:[NSLayoutConstraint constraintWithItem:_productImageView - attribute:NSLayoutAttributeLeft - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeLeft - multiplier:1.0f - constant:0]]; - [self.view addConstraint:[NSLayoutConstraint - constraintWithItem:_productImageView - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeWidth - multiplier:1 - constant:0]]; - [self.view addConstraint:[NSLayoutConstraint - constraintWithItem:_productImageView - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.view - attribute:NSLayoutAttributeBottom - multiplier:1 - constant:0]]; - [_productImageView - addConstraint:[NSLayoutConstraint - constraintWithItem:_productImageView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0f - constant:175]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_productImageView + attribute:NSLayoutAttributeTop + multiplier:1 + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeft + multiplier:1.0f + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeWidth + multiplier:1 + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeBottom + multiplier:1 + constant:0]]; + [_productImageView + addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0f + constant:175]]; } - (void)didReceiveNotification:(UNNotification *)notification { - [super didReceiveNotification:notification]; + [super didReceiveNotification:notification]; - _productNameLbl.text = - notification.request.content.userInfo[USER_INFO_PROD_NAME]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), - ^{ - NSURL *url = [NSURL - URLWithString:notification.request.content - .userInfo[USER_INFO_PROD_IMAGE_URL]]; - NSData *data = [NSData dataWithContentsOfURL:url]; - dispatch_async(dispatch_get_main_queue(), ^{ - _productImageView.image = [UIImage imageWithData:data]; - }); - }); + _productNameLbl.text = + notification.request.content.userInfo[USER_INFO_PROD_NAME]; + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSURL *url = + [NSURL URLWithString:notification.request.content + .userInfo[USER_INFO_PROD_IMAGE_URL]]; + NSData *data = [NSData dataWithContentsOfURL:url]; + dispatch_async(dispatch_get_main_queue(), ^{ + self->_productImageView.image = [UIImage imageWithData:data]; + }); + }); } - (ProductType)getProductType { - return ProductTypeProduct; + return ProductTypeProduct; } @end diff --git a/BVSDK/BVNotifications/BVProductReviewNotificationConfigurationLoader.m b/BVSDK/BVNotifications/BVProductReviewNotificationConfigurationLoader.m index f55fecc4..3f3473ba 100644 --- a/BVSDK/BVNotifications/BVProductReviewNotificationConfigurationLoader.m +++ b/BVSDK/BVNotifications/BVProductReviewNotificationConfigurationLoader.m @@ -77,7 +77,7 @@ - (void)loadPINConfiguration: [[BVLogger sharedLogger] verbose: @"Successfully loaded BVProductReviewNotificationProperties"]; - _bvProductReviewNotificationProperties = response; + self->_bvProductReviewNotificationProperties = response; completion(response); } diff --git a/BVSDK/BVNotifications/BVStoreNotificationConfigurationLoader.m b/BVSDK/BVNotifications/BVStoreNotificationConfigurationLoader.m index 82440db7..06d2faef 100644 --- a/BVSDK/BVNotifications/BVStoreNotificationConfigurationLoader.m +++ b/BVSDK/BVNotifications/BVStoreNotificationConfigurationLoader.m @@ -94,7 +94,7 @@ - (BOOL)isClientConfiguredForPush:(BVSDKManager *)sdkMgr { completion:^(BVStoreReviewNotificationProperties *__nonnull response) { [[BVLogger sharedLogger] verbose:@"Successfully loaded BVStoreReviewNotificationProperties"]; - _bvStoreReviewNotificationProperties = response; + self->_bvStoreReviewNotificationProperties = response; completion(response); } diff --git a/BVSDKTests/CommonTests/BVBaseStubTestCase.m b/BVSDKTests/CommonTests/BVBaseStubTestCase.m index 39c4fa9f..7bc6f94b 100644 --- a/BVSDKTests/CommonTests/BVBaseStubTestCase.m +++ b/BVSDKTests/CommonTests/BVBaseStubTestCase.m @@ -5,12 +5,12 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#define WaitForAllGroupsToBeEmpty(timeout) \ - do { \ - if (![self waitForGroupToBeEmptyWithTimeout:timeout]) { \ - XCTFail(@"Timed out waiting for groups to empty."); \ - } \ - } while (0) +#define WaitForAllGroupsToBeEmpty(timeout) \ + do { \ + if (![self waitForGroupToBeEmptyWithTimeout:timeout]) { \ + XCTFail(@"Timed out waiting for groups to empty."); \ + } \ + } while (0) #import "BVBaseStubTestCase.h" #import @@ -20,8 +20,8 @@ @interface BVBaseStubTestCase () // This is to synchronize on tests which have side-effects with other tests // being ran. It's basically a barrier between XCTestCases so that Xcode won't // enqueue new tests until these tests complete. -@property(nonatomic) dispatch_group_t barrier; -@property(nonatomic, readonly) dispatch_queue_t barrierQueue; +@property (nonatomic) dispatch_group_t barrier; +@property (nonatomic, readonly) dispatch_queue_t barrierQueue; @end @implementation BVBaseStubTestCase @@ -29,115 +29,118 @@ @implementation BVBaseStubTestCase @synthesize barrier = _barrier, barrierQueue = _barrierQueue; - (dispatch_queue_t)barrierQueue { - if (NULL == _barrierQueue) { - _barrierQueue = dispatch_queue_create( - "com.bazaarvoice.BVSDKTest.barrierQueue", DISPATCH_QUEUE_SERIAL); - } - return _barrierQueue; + if (NULL == _barrierQueue) { + _barrierQueue = dispatch_queue_create( + "com.bazaarvoice.BVSDKTest.barrierQueue", DISPATCH_QUEUE_SERIAL); + } + return _barrierQueue; } - (dispatch_group_t)barrier { - __block dispatch_group_t tempBarrier = NULL; - dispatch_sync(self.barrierQueue, ^{ - tempBarrier = _barrier; - }); - return tempBarrier; + __block dispatch_group_t tempBarrier = NULL; + dispatch_sync(self.barrierQueue, ^{ + tempBarrier = self->_barrier; + }); + return tempBarrier; } - (void)setBarrier:(dispatch_group_t)barrier { - dispatch_sync(self.barrierQueue, ^{ - _barrier = barrier; - }); + dispatch_sync(self.barrierQueue, ^{ + self->_barrier = barrier; + }); } - (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each - // test method in the class. + [super setUp]; + // Put setup code here. This method is called before the invocation of each + // test method in the class. } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each - // test method in the class. - [super tearDown]; - [OHHTTPStubs removeAllStubs]; + // Put teardown code here. This method is called after the invocation of + // each + // test method in the class. + [super tearDown]; + [OHHTTPStubs removeAllStubs]; } - (void)addStubWith200ResponseForJSONFileNamed:(NSString *)resultFile { - [self addStubWith200ResponseForJSONFileNamed:resultFile withPassingTest:nil]; + [self addStubWith200ResponseForJSONFileNamed:resultFile + withPassingTest:nil]; } - (void)addStubWith200ResponseForJSONFileNamed:(nonnull NSString *)resultFile withPassingTest: (nullable OHHTTPStubsTestBlock)testBlock { - [self addStubWith200ResponseForJSONFilesNamed:@[ resultFile ] - withPassingTest:testBlock]; + [self addStubWith200ResponseForJSONFilesNamed:@[ resultFile ] + withPassingTest:testBlock]; } - (void)addStubWith200ResponseForJSONFilesNamed: (nonnull NSArray *)resultFileArray { - [self addStubWith200ResponseForJSONFilesNamed:resultFileArray - withPassingTest:nil]; + [self addStubWith200ResponseForJSONFilesNamed:resultFileArray + withPassingTest:nil]; } - (void)addStubWith200ResponseForJSONFilesNamed: (nonnull NSArray *)resultFileArray withPassingTest: (nullable OHHTTPStubsTestBlock)testBlock { - - __block NSUInteger callCount = 0; - NSUInteger fileCount = resultFileArray.count; - OHHTTPStubsTestBlock passableTest = - testBlock ?: ^BOOL(NSURLRequest *request) { - return [request.URL.host containsString:@"bazaarvoice.com"]; - }; - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return passableTest(request); - } - withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - - if (callCount < fileCount) { - id fileObj = [resultFileArray objectAtIndex:callCount]; - if (__ISA(fileObj, NSString)) { - NSString *resultFile = (NSString *)fileObj; - - // Increment count - callCount++; - - return [[OHHTTPStubsResponse - responseWithFileAtPath:OHPathForFile(resultFile, self.class) - statusCode:200 - headers:@{ - @"Content-Type" : - @"application/json;charset=utf-8" - }] responseTime:OHHTTPStubsDownloadSpeedWifi]; + __block NSUInteger callCount = 0; + NSUInteger fileCount = resultFileArray.count; + OHHTTPStubsTestBlock passableTest = + testBlock ?: ^BOOL(NSURLRequest *request) { + return [request.URL.host containsString:@"bazaarvoice.com"]; + }; + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return passableTest(request); + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + + if (callCount < fileCount) { + id fileObj = [resultFileArray objectAtIndex:callCount]; + if (__ISA(fileObj, NSString)) { + NSString *resultFile = (NSString *)fileObj; + + // Increment count + callCount++; + + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(resultFile, + self.class) + statusCode:200 + headers:@{ + @"Content-Type" : + @"application/json;charset=utf-8" + }] + responseTime:OHHTTPStubsDownloadSpeedWifi]; + } } - } - - NSError *resourceUnavailableError = - [NSError errorWithDomain:NSURLErrorDomain - code:kCFURLErrorResourceUnavailable - userInfo:nil]; - return [OHHTTPStubsResponse responseWithError:resourceUnavailableError]; - }]; + + NSError *resourceUnavailableError = + [NSError errorWithDomain:NSURLErrorDomain + code:kCFURLErrorResourceUnavailable + userInfo:nil]; + return + [OHHTTPStubsResponse responseWithError:resourceUnavailableError]; + }]; } - (void)addStubWithResultFile:(NSString *)resultFile statusCode:(NSInteger)httpStatus withHeaders:(NSDictionary *)httpHeaders { - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.host containsString:@"bazaarvoice.com"]; - } - withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { - // return normal user profile from /users API - return [[OHHTTPStubsResponse - responseWithFileAtPath:OHPathForFile(resultFile, self.class) - statusCode:(int)httpStatus - headers:httpHeaders] - responseTime:OHHTTPStubsDownloadSpeedWifi]; - }]; + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return [request.URL.host containsString:@"bazaarvoice.com"]; + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { + // return normal user profile from /users API + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(resultFile, self.class) + statusCode:(int)httpStatus + headers:httpHeaders] + responseTime:OHHTTPStubsDownloadSpeedWifi]; + }]; } #pragma mark - Synchronization Primitives @@ -146,44 +149,43 @@ - (void)addStubWithResultFile:(NSString *)resultFile * Thanks to the people at obj.io: https://www.objc.io/issues/15-testing/xctest/ */ - (BOOL)isBarrierHeld { - return (NULL != self.barrier); + return (NULL != self.barrier); } - (void)retainBarrier { - if (NULL == self.barrier) { - self.barrier = dispatch_group_create(); - } - dispatch_group_enter(self.barrier); + if (NULL == self.barrier) { + self.barrier = dispatch_group_create(); + } + dispatch_group_enter(self.barrier); } - (void)releaseBarrier { - if (NULL != self.barrier) { - dispatch_group_leave(self.barrier); - } + if (NULL != self.barrier) { + dispatch_group_leave(self.barrier); + } } - (void)forceWaitForBarrierWithTimeout:(NSTimeInterval)barrierTimeout { - (void)[self waitForGroupToBeEmptyWithTimeout:barrierTimeout]; + (void)[self waitForGroupToBeEmptyWithTimeout:barrierTimeout]; } - (BOOL)waitForGroupToBeEmptyWithTimeout:(NSTimeInterval)timeout { + if (NULL == self.barrier) { + return YES; + } - if (NULL == self.barrier) { - return YES; - } - - NSDate *const end = [[NSDate date] dateByAddingTimeInterval:timeout]; - int64_t delta = (int64_t)timeout; - dispatch_time_t dispatchTimeout = dispatch_time(DISPATCH_TIME_NOW, delta); + NSDate *const end = [[NSDate date] dateByAddingTimeInterval:timeout]; + int64_t delta = (int64_t)timeout; + dispatch_time_t dispatchTimeout = dispatch_time(DISPATCH_TIME_NOW, delta); - __block BOOL didComplete = NO; - dispatch_group_notify(self.barrier, dispatch_get_main_queue(), ^{ - didComplete = YES; - }); + __block BOOL didComplete = NO; + dispatch_group_notify(self.barrier, dispatch_get_main_queue(), ^{ + didComplete = YES; + }); - dispatch_group_wait(self.barrier, dispatchTimeout); + dispatch_group_wait(self.barrier, dispatchTimeout); - return didComplete && (0. < [end timeIntervalSinceNow]); + return didComplete && (0. < [end timeIntervalSinceNow]); } @end diff --git a/BVSDKTests/CommonTests/BVNetworkDelegateTestsDelegate.m b/BVSDKTests/CommonTests/BVNetworkDelegateTestsDelegate.m index 074a5375..666769e6 100644 --- a/BVSDKTests/CommonTests/BVNetworkDelegateTestsDelegate.m +++ b/BVSDKTests/CommonTests/BVNetworkDelegateTestsDelegate.m @@ -23,21 +23,21 @@ @implementation BVNetworkDelegateTestsDelegate - (XCTestExpectation *)urlSessionExpectation { __block XCTestExpectation *tempExpectation = nil; dispatch_sync(self.blockQueue, ^{ - tempExpectation = _urlSessionExpectation; + tempExpectation = self->_urlSessionExpectation; }); return tempExpectation; } - (void)setUrlSessionExpectation:(XCTestExpectation *)urlSessionExpectation { dispatch_sync(self.blockQueue, ^{ - _urlSessionExpectation = urlSessionExpectation; + self->_urlSessionExpectation = urlSessionExpectation; }); } - (XCTestExpectation *)urlSessionTaskExpectation { __block XCTestExpectation *tempExpectation = nil; dispatch_sync(self.blockQueue, ^{ - tempExpectation = _urlSessionTaskExpectation; + tempExpectation = self->_urlSessionTaskExpectation; }); return tempExpectation; } @@ -45,7 +45,7 @@ - (XCTestExpectation *)urlSessionTaskExpectation { - (void)setUrlSessionTaskExpectation: (XCTestExpectation *)urlSessionTaskExpectation { dispatch_sync(self.blockQueue, ^{ - _urlSessionTaskExpectation = urlSessionTaskExpectation; + self->_urlSessionTaskExpectation = urlSessionTaskExpectation; }); } diff --git a/BVSDKTests/ConversationsTests/SubmissionTests/UASSubmissionTests.swift b/BVSDKTests/ConversationsTests/SubmissionTests/UASSubmissionTests.swift index 08a1a6e6..54c07519 100644 --- a/BVSDKTests/ConversationsTests/SubmissionTests/UASSubmissionTests.swift +++ b/BVSDKTests/ConversationsTests/SubmissionTests/UASSubmissionTests.swift @@ -6,7 +6,6 @@ // import XCTest -import CommonCrypto @testable import BVSDK class UASSubmissionTests: XCTestCase { diff --git a/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj b/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj index cf5e84be..552475f8 100644 --- a/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj +++ b/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/project.pbxproj @@ -850,7 +850,6 @@ 73617B7C1CFF38970063BD5F /* Twitter Fabric build script */, 871AD3F11D05FC25006583E5 /* Reset Plist */, 21ACCC09CDFDC5CB4CDC4A48 /* [CP] Embed Pods Frameworks */, - 4DF8543FD7554FD06D442633 /* [CP] Copy Pods Resources */, 878D4C631DD66BD400430654 /* Embed Frameworks */, ); buildRules = ( @@ -889,7 +888,6 @@ 7385D84B1CE3ACD700CA5F5E /* Sources */, 7385D84C1CE3ACD700CA5F5E /* Frameworks */, 7385D84D1CE3ACD700CA5F5E /* Resources */, - FEB7E0F04DA96A9E8D25ABF1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -907,7 +905,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0810; - LastUpgradeCheck = 0910; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 73199AE91CDD84E4006CC59D = { @@ -1108,21 +1106,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BVSDKDemo/Pods-BVSDKDemo-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 4DF8543FD7554FD06D442633 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BVSDKDemo/Pods-BVSDKDemo-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 730A09F11D01F3EF00E71C3B /* Import Hidden Keys */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1216,21 +1199,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - FEB7E0F04DA96A9E8D25ABF1 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Curations Custom Post Extension/Pods-Curations Custom Post Extension-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1372,12 +1340,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1425,12 +1395,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/xcshareddata/xcschemes/BVSDKDemo.xcscheme b/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/xcshareddata/xcschemes/BVSDKDemo.xcscheme index 2cb15e2b..27a73224 100644 --- a/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/xcshareddata/xcschemes/BVSDKDemo.xcscheme +++ b/Examples/BVSDKDemo/BVSDKDemo.xcodeproj/xcshareddata/xcschemes/BVSDKDemo.xcscheme @@ -1,6 +1,6 @@ FAKFontAwesome!)) { + func setCustomLeftIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) { leftIcon.image = getIconImage(icon) } - func setCustomRightIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!)) { + func setCustomRightIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) { rightIcon.image = getIconImage(icon) } - func getIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!)) -> UIImage { + func getIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) -> UIImage { let size = CGFloat(20) diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h index e0362991..949d0a75 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h @@ -12,7 +12,7 @@ @import MapKit; -typedef void (^ActionBlock)(); +typedef void (^ActionBlock)(void); @interface JPSThumbnail : NSObject diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m index 14cf4fc5..9b3c1a2c 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m @@ -10,54 +10,55 @@ @interface JPSThumbnailAnnotation () -@property(nonatomic, readwrite) JPSThumbnailAnnotationView *view; -@property(nonatomic, readonly) JPSThumbnail *thumbnail; +@property (nonatomic, readwrite) JPSThumbnailAnnotationView *view; +@property (nonatomic, readonly) JPSThumbnail *thumbnail; @end @implementation JPSThumbnailAnnotation + (instancetype)annotationWithThumbnail:(JPSThumbnail *)thumbnail { - return [[self alloc] initWithThumbnail:thumbnail]; + return [[self alloc] initWithThumbnail:thumbnail]; } - (id)initWithThumbnail:(JPSThumbnail *)thumbnail { - self = [super init]; - if (self) { - _coordinate = thumbnail.coordinate; - _thumbnail = thumbnail; - } - return self; + self = [super init]; + if (self) { + _coordinate = thumbnail.coordinate; + _thumbnail = thumbnail; + } + return self; } - (MKAnnotationView *)annotationViewInMap:(MKMapView *)mapView { - if (!self.view) { - self.view = (JPSThumbnailAnnotationView *)[mapView - dequeueReusableAnnotationViewWithIdentifier: - kJPSThumbnailAnnotationViewReuseID]; - if (!self.view) - self.view = [[JPSThumbnailAnnotationView alloc] initWithAnnotation:self]; - } else { - self.view.annotation = self; - } - [self updateThumbnail:self.thumbnail animated:NO]; - return self.view; + if (!self.view) { + self.view = (JPSThumbnailAnnotationView *)[mapView + dequeueReusableAnnotationViewWithIdentifier: + kJPSThumbnailAnnotationViewReuseID]; + if (!self.view) + self.view = + [[JPSThumbnailAnnotationView alloc] initWithAnnotation:self]; + } else { + self.view.annotation = self; + } + [self updateThumbnail:self.thumbnail animated:NO]; + return self.view; } - (void)updateThumbnail:(JPSThumbnail *)thumbnail animated:(BOOL)animated { - if (animated) { - [UIView - animateWithDuration:0.33f - animations:^{ - _coordinate = - thumbnail - .coordinate; // use ivar to avoid triggering setter - }]; - } else { - _coordinate = thumbnail.coordinate; // use ivar to avoid triggering setter - } - - [self.view updateWithThumbnail:thumbnail]; + if (animated) { + [UIView animateWithDuration:0.33f + animations:^{ + self->_coordinate = + thumbnail.coordinate; // use ivar to avoid + // triggering setter + }]; + } else { + _coordinate = + thumbnail.coordinate; // use ivar to avoid triggering setter + } + + [self.view updateWithThumbnail:thumbnail]; } @end diff --git a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Frameworks/SweetAlert.swift b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Frameworks/SweetAlert.swift index bfa312e5..580b94a1 100755 --- a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Frameworks/SweetAlert.swift +++ b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Frameworks/SweetAlert.swift @@ -355,7 +355,7 @@ class CancelAnimatedView: AnimatableView { setupLayers() var t = CATransform3DIdentity; t.m34 = 1.0 / -500.0; - t = CATransform3DRotate(t, CGFloat(90.0 * M_PI / 180.0), 1, 0, 0); + t = CATransform3DRotate(t, CGFloat(90.0 * Double.pi / 180.0), 1, 0, 0); circleLayer.transform = t crossPathLayer.opacity = 0.0 } @@ -370,8 +370,8 @@ class CancelAnimatedView: AnimatableView { private var outlineCircle: CGPath { let path = UIBezierPath() - let startAngle: CGFloat = CGFloat((0) / 180.0 * M_PI) //0 - let endAngle: CGFloat = CGFloat((360) / 180.0 * M_PI) //360 + let startAngle: CGFloat = CGFloat((0) / 180.0 * Double.pi) //0 + let endAngle: CGFloat = CGFloat((360) / 180.0 * Double.pi) //360 path.addArc(withCenter: CGPoint(x: self.frame.size.width/2.0, y: self.frame.size.width/2.0), radius: self.frame.size.width/2.0, startAngle: startAngle, endAngle: endAngle, clockwise: false) return path.cgPath @@ -412,11 +412,11 @@ class CancelAnimatedView: AnimatableView { override func animate() { var t = CATransform3DIdentity; t.m34 = 1.0 / -500.0; - t = CATransform3DRotate(t, CGFloat(90.0 * M_PI / 180.0), 1, 0, 0); + t = CATransform3DRotate(t, CGFloat(90.0 * Double.pi / 180.0), 1, 0, 0); var t2 = CATransform3DIdentity; t2.m34 = 1.0 / -500.0; - t2 = CATransform3DRotate(t2, CGFloat(-M_PI), 1, 0, 0); + t2 = CATransform3DRotate(t2, CGFloat(-Double.pi), 1, 0, 0); let animation = CABasicAnimation(keyPath: "transform") let time = 0.3 @@ -472,8 +472,8 @@ class InfoAnimatedView: AnimatableView { var outlineCircle: CGPath { let path = UIBezierPath() - let startAngle: CGFloat = CGFloat((0) / 180.0 * M_PI) //0 - let endAngle: CGFloat = CGFloat((360) / 180.0 * M_PI) //360 + let startAngle: CGFloat = CGFloat((0) / 180.0 * Double.pi) //0 + let endAngle: CGFloat = CGFloat((360) / 180.0 * Double.pi) //360 path.addArc(withCenter: CGPoint(x: self.frame.size.width/2.0, y: self.frame.size.width/2.0), radius: self.frame.size.width/2.0, startAngle: startAngle, endAngle: endAngle, clockwise: false) let factor:CGFloat = self.frame.size.width / 1.5 @@ -533,16 +533,16 @@ class SuccessAnimatedView: AnimatableView { var outlineCircle: CGPath { let path = UIBezierPath() - let startAngle: CGFloat = CGFloat((0) / 180.0 * M_PI) //0 - let endAngle: CGFloat = CGFloat((360) / 180.0 * M_PI) //360 + let startAngle: CGFloat = CGFloat((0) / 180.0 * Double.pi) //0 + let endAngle: CGFloat = CGFloat((360) / 180.0 * Double.pi) //360 path.addArc(withCenter: CGPoint(x: self.frame.size.width/2.0, y: self.frame.size.height/2.0), radius: self.frame.size.width/2.0, startAngle: startAngle, endAngle: endAngle, clockwise: false) return path.cgPath } var path: CGPath { let path = UIBezierPath() - let startAngle:CGFloat = CGFloat((60) / 180.0 * M_PI) //60 - let endAngle:CGFloat = CGFloat((200) / 180.0 * M_PI) //190 + let startAngle:CGFloat = CGFloat((60) / 180.0 * Double.pi) //60 + let endAngle:CGFloat = CGFloat((200) / 180.0 * Double.pi) //190 path.addArc(withCenter: CGPoint(x: self.frame.size.width/2.0, y: self.frame.size.height/2.0), radius: self.frame.size.width/2.0, startAngle: startAngle, endAngle: endAngle, clockwise: false) path.addLine(to: CGPoint(x: 36.0 - 10.0 ,y: 60.0 - 10.0)) path.addLine(to: CGPoint(x: 85.0 - 20.0, y: 30.0 - 20.0)) diff --git a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Util.swift b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Util.swift index ecf2babb..88df7c09 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Util.swift +++ b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/Util.swift @@ -39,14 +39,14 @@ class Util: NSObject { } /// Get a default light grey icon - static func getFontAwesomeIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!)) -> UIImage { + static func getFontAwesomeIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) -> UIImage { return self.getFontAwesomeIconImage(icon, color: UIColor.lightGray, alpha: 0.5, size: 20) } /// Get an icon with specified size, color, and alpha - static func getFontAwesomeIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!), + static func getFontAwesomeIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?), color : UIColor, alpha : CGFloat, size : CGFloat) -> UIImage { diff --git a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/ProductPageButtonCell.swift b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/ProductPageButtonCell.swift index 2754e6ab..8e3d720e 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/ProductPageButtonCell.swift +++ b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/ProductPageButtonCell.swift @@ -17,15 +17,15 @@ class ProductPageButtonCell: UITableViewCell { @IBOutlet weak var leftIcon : UIImageView! - func setCustomLeftIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!)) { + func setCustomLeftIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) { leftIcon.image = getIconImage(icon) } - func setCustomRightIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!)) { + func setCustomRightIcon(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) { rightIcon.image = getIconImage(icon) } - func getIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!)) -> UIImage { + func getIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?)) -> UIImage { let size = CGFloat(20) diff --git a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/RatingTableViewCell.swift b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/RatingTableViewCell.swift index e118dc8b..5d816336 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/RatingTableViewCell.swift +++ b/Examples/BVSDKDemo/BVSDKDemo/Product Recommendations/View Controllers/RatingTableViewCell.swift @@ -82,7 +82,7 @@ class RatingTableViewCell: BVReviewTableViewCell { } - func getIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome!), color: UIColor) -> UIImage { + func getIconImage(_ icon : ((_ size: CGFloat) -> FAKFontAwesome?), color: UIColor) -> UIImage { let size = CGFloat(22) diff --git a/Examples/BVSDKDemo/Podfile.lock b/Examples/BVSDKDemo/Podfile.lock index fff2af29..0dfc3ff9 100644 --- a/Examples/BVSDKDemo/Podfile.lock +++ b/Examples/BVSDKDemo/Podfile.lock @@ -91,9 +91,27 @@ DEPENDENCIES: - XLActionController (~> 3.0) - youtube-ios-player-helper +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - Bolts + - Crashlytics + - Fabric + - FBSDKCoreKit + - FBSDKLoginKit + - FBSDKShareKit + - FontAwesomeKit + - Google-Mobile-Ads-SDK + - HCSStarRatingView + - NVActivityIndicatorView + - OHHTTPStubs + - SDWebImage + - SwiftyJSON + - XLActionController + - youtube-ios-player-helper + EXTERNAL SOURCES: BVSDK: - :path: ../../ + :path: "../../" SDForms: :branch: master :git: https://github.com/austimkelly/SDForms.git @@ -124,4 +142,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: f2145d845c38000175311cab40ce05f8499461d6 -COCOAPODS: 1.4.0 +COCOAPODS: 1.5.2 diff --git a/Examples/Conversations/Obj-C/ConversationsExample.xcodeproj/project.pbxproj b/Examples/Conversations/Obj-C/ConversationsExample.xcodeproj/project.pbxproj index ead26bb6..f06ef756 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample.xcodeproj/project.pbxproj +++ b/Examples/Conversations/Obj-C/ConversationsExample.xcodeproj/project.pbxproj @@ -220,7 +220,6 @@ 876DF5511CF4E87A005D841F /* Frameworks */, 876DF5521CF4E87A005D841F /* Resources */, 28C77DE22A8AE2C47581085E /* [CP] Embed Pods Frameworks */, - 92A40C857D267ABE8E9A62C4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -237,7 +236,7 @@ 876DF54C1CF4E87A005D841F /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 876DF5531CF4E87A005D841F = { @@ -322,21 +321,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 92A40C857D267ABE8E9A62C4 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConversationsExample/Pods-ConversationsExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -392,14 +376,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -438,14 +430,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m index e0f45ae5..7c03cfe4 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m @@ -74,7 +74,7 @@ - (void)viewDidLoad { // Success! NSLog(@"Succesfully loaded profile: %@", response); self.authorResponse = response; - [_authorProfileTableView reloadData]; + [self->_authorProfileTableView reloadData]; } failure:^(NSArray *_Nonnull errors) { diff --git a/Examples/Conversations/Obj-C/Podfile.lock b/Examples/Conversations/Obj-C/Podfile.lock index 68c646c1..aff226e1 100644 --- a/Examples/Conversations/Obj-C/Podfile.lock +++ b/Examples/Conversations/Obj-C/Podfile.lock @@ -1,11 +1,11 @@ PODS: - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVCommonUI (6.9.0) - - BVSDK/BVConversations (6.9.0): + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVCommonUI (7.0.0) + - BVSDK/BVConversations (7.0.0): - BVSDK/BVCommon - - BVSDK/BVConversationsStores (6.9.0): + - BVSDK/BVConversationsStores (7.0.0): - BVSDK/BVConversations - - BVSDK/BVConversationsUI (6.9.0): + - BVSDK/BVConversationsUI (7.0.0): - BVSDK/BVCommonUI - BVSDK/BVConversationsStores @@ -16,11 +16,11 @@ DEPENDENCIES: EXTERNAL SOURCES: BVSDK: - :path: ../../../ + :path: "../../../" SPEC CHECKSUMS: - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a PODFILE CHECKSUM: 559e53a6ad617be48267d91baa012b393854e813 -COCOAPODS: 1.3.1 +COCOAPODS: 1.5.2 diff --git a/Examples/Conversations/Swift/ConversationsExample.xcodeproj/project.pbxproj b/Examples/Conversations/Swift/ConversationsExample.xcodeproj/project.pbxproj index 08ff1374..7f16e695 100644 --- a/Examples/Conversations/Swift/ConversationsExample.xcodeproj/project.pbxproj +++ b/Examples/Conversations/Swift/ConversationsExample.xcodeproj/project.pbxproj @@ -184,7 +184,6 @@ 879143EB1CF4F05C00976220 /* Frameworks */, 879143EC1CF4F05C00976220 /* Resources */, B3F8ED540AF86038229D739F /* [CP] Embed Pods Frameworks */, - D45BE515DBEC9B6114B20DBE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -202,11 +201,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 879143ED1CF4F05C00976220 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0940; }; }; }; @@ -285,21 +285,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConversationsExample/Pods-ConversationsExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - D45BE515DBEC9B6114B20DBE /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConversationsExample/Pods-ConversationsExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -356,14 +341,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -404,14 +397,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -447,7 +448,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.ConversationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -461,7 +462,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.ConversationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Examples/Conversations/Swift/Podfile.lock b/Examples/Conversations/Swift/Podfile.lock index 7ab7ed11..557cfb86 100644 --- a/Examples/Conversations/Swift/Podfile.lock +++ b/Examples/Conversations/Swift/Podfile.lock @@ -1,11 +1,11 @@ PODS: - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVCommonUI (6.9.0) - - BVSDK/BVConversations (6.9.0): + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVCommonUI (7.0.0) + - BVSDK/BVConversations (7.0.0): - BVSDK/BVCommon - - BVSDK/BVConversationsStores (6.9.0): + - BVSDK/BVConversationsStores (7.0.0): - BVSDK/BVConversations - - BVSDK/BVConversationsUI (6.9.0): + - BVSDK/BVConversationsUI (7.0.0): - BVSDK/BVCommonUI - BVSDK/BVConversationsStores @@ -16,11 +16,11 @@ DEPENDENCIES: EXTERNAL SOURCES: BVSDK: - :path: ../../../ + :path: "../../../" SPEC CHECKSUMS: - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a PODFILE CHECKSUM: 5f046e8508ae294ed9a88db12438549234c28c85 -COCOAPODS: 1.3.1 +COCOAPODS: 1.5.2 diff --git a/Examples/Curations/Obj-C/CurationsExample.xcodeproj/project.pbxproj b/Examples/Curations/Obj-C/CurationsExample.xcodeproj/project.pbxproj index 9ca648c4..3a91150d 100644 --- a/Examples/Curations/Obj-C/CurationsExample.xcodeproj/project.pbxproj +++ b/Examples/Curations/Obj-C/CurationsExample.xcodeproj/project.pbxproj @@ -127,7 +127,6 @@ 879144091CF4F67D00976220 /* Frameworks */, 8791440A1CF4F67D00976220 /* Resources */, 7A7AA38C572BF506740B4B20 /* [CP] Embed Pods Frameworks */, - BA4B06C072A56930820B5CED /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -144,7 +143,7 @@ 879144041CF4F67D00976220 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 8791440B1CF4F67D00976220 = { @@ -226,21 +225,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CurationsExample/Pods-CurationsExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - BA4B06C072A56930820B5CED /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CurationsExample/Pods-CurationsExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -285,14 +269,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -332,14 +324,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Examples/Curations/Obj-C/Podfile.lock b/Examples/Curations/Obj-C/Podfile.lock index 7c427bc7..154cfb9c 100644 --- a/Examples/Curations/Obj-C/Podfile.lock +++ b/Examples/Curations/Obj-C/Podfile.lock @@ -1,9 +1,9 @@ PODS: - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVCommonUI (6.9.0) - - BVSDK/BVCurations (6.9.0): + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVCommonUI (7.0.0) + - BVSDK/BVCurations (7.0.0): - BVSDK/BVCommon - - BVSDK/BVCurationsUI (6.9.0): + - BVSDK/BVCurationsUI (7.0.0): - BVSDK/BVCommonUI - BVSDK/BVCurations - SDWebImage (4.0.0): @@ -15,14 +15,18 @@ DEPENDENCIES: - BVSDK/BVCurationsUI (from `../../../`) - SDWebImage +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - SDWebImage + EXTERNAL SOURCES: BVSDK: - :path: ../../../ + :path: "../../../" SPEC CHECKSUMS: - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a SDWebImage: 76a6348bdc74eb5a55dd08a091ef298e56b55e41 PODFILE CHECKSUM: cdc1a54abccc6f1bd449642f2d541237ae6dc4ab -COCOAPODS: 1.3.1 +COCOAPODS: 1.5.2 diff --git a/Examples/Curations/Swift/CurationsExample.xcodeproj/project.pbxproj b/Examples/Curations/Swift/CurationsExample.xcodeproj/project.pbxproj index 8e4e7518..5089e592 100644 --- a/Examples/Curations/Swift/CurationsExample.xcodeproj/project.pbxproj +++ b/Examples/Curations/Swift/CurationsExample.xcodeproj/project.pbxproj @@ -112,7 +112,6 @@ 879144331CF5DE6700976220 /* Frameworks */, 879144341CF5DE6700976220 /* Resources */, E09C4171092875BF599ECCD8 /* [CP] Embed Pods Frameworks */, - 4FBE78E5EE9A68C1E6A379FE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -130,12 +129,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 879144351CF5DE6700976220 = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0820; + LastSwiftMigration = 0940; }; }; }; @@ -193,21 +192,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 4FBE78E5EE9A68C1E6A379FE /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CurationsExample/Pods-CurationsExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; E09C4171092875BF599ECCD8 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -271,13 +255,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -317,13 +311,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -342,6 +346,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -357,7 +362,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.CurationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -371,7 +376,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.CurationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Examples/Curations/Swift/CurationsExample/Base.lproj/LaunchScreen.storyboard b/Examples/Curations/Swift/CurationsExample/Base.lproj/LaunchScreen.storyboard index 2e721e18..12baca9e 100644 --- a/Examples/Curations/Swift/CurationsExample/Base.lproj/LaunchScreen.storyboard +++ b/Examples/Curations/Swift/CurationsExample/Base.lproj/LaunchScreen.storyboard @@ -1,7 +1,12 @@ - - + + + + + - + + + @@ -13,10 +18,9 @@ - + - - + diff --git a/Examples/Curations/Swift/CurationsExample/ViewController.swift b/Examples/Curations/Swift/CurationsExample/ViewController.swift index 9089c80a..55e10ce7 100644 --- a/Examples/Curations/Swift/CurationsExample/ViewController.swift +++ b/Examples/Curations/Swift/CurationsExample/ViewController.swift @@ -40,7 +40,7 @@ class ViewController: UIViewController, BVCurationsUICollectionViewDelegate { } // This demo shows how to create a customized Share View Controller and upload an image and text to Curations. - func didTapAddPhotoButton(_ sender: AnyObject) { + @objc func didTapAddPhotoButton(_ sender: AnyObject) { // Here we load our request with the groups we want to subit to and additional info. let shareRequest = BVCurationsAddPostRequest(groups: [], diff --git a/Examples/Curations/Swift/Podfile.lock b/Examples/Curations/Swift/Podfile.lock index 7c427bc7..3ef3b152 100644 --- a/Examples/Curations/Swift/Podfile.lock +++ b/Examples/Curations/Swift/Podfile.lock @@ -1,28 +1,32 @@ PODS: - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVCommonUI (6.9.0) - - BVSDK/BVCurations (6.9.0): + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVCommonUI (7.0.0) + - BVSDK/BVCurations (7.0.0): - BVSDK/BVCommon - - BVSDK/BVCurationsUI (6.9.0): + - BVSDK/BVCurationsUI (7.0.0): - BVSDK/BVCommonUI - BVSDK/BVCurations - - SDWebImage (4.0.0): - - SDWebImage/Core (= 4.0.0) - - SDWebImage/Core (4.0.0) + - SDWebImage (4.3.2): + - SDWebImage/Core (= 4.3.2) + - SDWebImage/Core (4.3.2) DEPENDENCIES: - BVSDK/BVCurations (from `../../../`) - BVSDK/BVCurationsUI (from `../../../`) - SDWebImage +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - SDWebImage + EXTERNAL SOURCES: BVSDK: - :path: ../../../ + :path: "../../../" SPEC CHECKSUMS: - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 - SDWebImage: 76a6348bdc74eb5a55dd08a091ef298e56b55e41 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a + SDWebImage: 29bd3c0bc1f01e5eb59720847c64ad4c4ca1af5d PODFILE CHECKSUM: cdc1a54abccc6f1bd449642f2d541237ae6dc4ab -COCOAPODS: 1.3.1 +COCOAPODS: 1.5.2 diff --git a/Examples/ProductRecommendations/Obj-C/Podfile.lock b/Examples/ProductRecommendations/Obj-C/Podfile.lock index 04e0c273..226f0a96 100644 --- a/Examples/ProductRecommendations/Obj-C/Podfile.lock +++ b/Examples/ProductRecommendations/Obj-C/Podfile.lock @@ -1,6 +1,6 @@ PODS: - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVRecommendations (6.9.0): + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVRecommendations (7.0.0): - BVSDK/BVCommon - HCSStarRatingView (1.4.5) - SDWebImage (3.7.6): @@ -12,15 +12,20 @@ DEPENDENCIES: - HCSStarRatingView - SDWebImage +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - HCSStarRatingView + - SDWebImage + EXTERNAL SOURCES: BVSDK: - :path: ../../../ + :path: "../../../" SPEC CHECKSUMS: - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a HCSStarRatingView: bff3f89314d3b5013ce826ea11897d3129ff6b8c SDWebImage: c325cf02c30337336b95beff20a13df489ec0ec9 PODFILE CHECKSUM: 566be901e50ff5a9bc847db992598ad522bbe31c -COCOAPODS: 1.3.1 +COCOAPODS: 1.5.2 diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample.xcodeproj/project.pbxproj b/Examples/ProductRecommendations/Obj-C/RecommendationsExample.xcodeproj/project.pbxproj index b82ac2ba..b83794e7 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample.xcodeproj/project.pbxproj +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample.xcodeproj/project.pbxproj @@ -129,7 +129,6 @@ 879144551CF5E4AE00976220 /* Frameworks */, 879144561CF5E4AE00976220 /* Resources */, 822260AC698624A40FA436FD /* [CP] Embed Pods Frameworks */, - 55346D279A233AAAD83E1836 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -146,7 +145,7 @@ 879144501CF5E4AE00976220 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 879144571CF5E4AE00976220 = { @@ -189,21 +188,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 55346D279A233AAAD83E1836 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RecommendationsExample/Pods-RecommendationsExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 822260AC698624A40FA436FD /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -289,14 +273,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -336,14 +328,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Examples/ProductRecommendations/Swift/Podfile.lock b/Examples/ProductRecommendations/Swift/Podfile.lock index 2509a669..8d0db283 100644 --- a/Examples/ProductRecommendations/Swift/Podfile.lock +++ b/Examples/ProductRecommendations/Swift/Podfile.lock @@ -1,6 +1,6 @@ PODS: - - BVSDK/BVCommon (6.9.0) - - BVSDK/BVRecommendations (6.9.0): + - BVSDK/BVCommon (7.0.0) + - BVSDK/BVRecommendations (7.0.0): - BVSDK/BVCommon - HCSStarRatingView (1.4.5) - SDWebImage (3.7.6): @@ -12,15 +12,20 @@ DEPENDENCIES: - HCSStarRatingView - SDWebImage +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - HCSStarRatingView + - SDWebImage + EXTERNAL SOURCES: BVSDK: - :path: ../../../ + :path: "../../../" SPEC CHECKSUMS: - BVSDK: b9c9bd6679ea67b31cd3fbd19835a145fa36aae0 + BVSDK: aa21c06aafe277c00e47882fbc0df77e7bd02e2a HCSStarRatingView: bff3f89314d3b5013ce826ea11897d3129ff6b8c SDWebImage: c325cf02c30337336b95beff20a13df489ec0ec9 PODFILE CHECKSUM: 23b5c529088c467c15f5d6c9e70330e11393ccd0 -COCOAPODS: 1.3.1 +COCOAPODS: 1.5.2 diff --git a/Examples/ProductRecommendations/Swift/RecommendationsExample.xcodeproj/project.pbxproj b/Examples/ProductRecommendations/Swift/RecommendationsExample.xcodeproj/project.pbxproj index 08ef5b7f..445a4651 100644 --- a/Examples/ProductRecommendations/Swift/RecommendationsExample.xcodeproj/project.pbxproj +++ b/Examples/ProductRecommendations/Swift/RecommendationsExample.xcodeproj/project.pbxproj @@ -112,7 +112,6 @@ 8791447D1CF6060900976220 /* Frameworks */, 8791447E1CF6060900976220 /* Resources */, EE7EA90E59B5DBC27E46A0C7 /* [CP] Embed Pods Frameworks */, - B069838054A2FAB9C9E0DAE1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -130,11 +129,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Bazaarvoice; TargetAttributes = { 8791447F1CF6060900976220 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0940; }; }; }; @@ -191,21 +191,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B069838054A2FAB9C9E0DAE1 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RecommendationsExample/Pods-RecommendationsExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; EE7EA90E59B5DBC27E46A0C7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -272,14 +257,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -320,14 +313,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -363,7 +364,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.RecommendationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -377,7 +378,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.RecommendationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Examples/ProductRecommendations/Swift/RecommendationsExample/DemoCell.swift b/Examples/ProductRecommendations/Swift/RecommendationsExample/DemoCell.swift index 875e575f..741dfc8d 100644 --- a/Examples/ProductRecommendations/Swift/RecommendationsExample/DemoCell.swift +++ b/Examples/ProductRecommendations/Swift/RecommendationsExample/DemoCell.swift @@ -10,29 +10,31 @@ import HCSStarRatingView import SDWebImage class DemoCell: BVRecommendationCollectionViewCell { + + @IBOutlet weak var productName : UILabel! + @IBOutlet weak var price : UILabel! + @IBOutlet weak var numReview : UILabel! + @IBOutlet weak var rating : UILabel! + @IBOutlet weak var productImageView : UIImageView! + @IBOutlet weak var starRating : HCSStarRatingView! + + override var bvRecommendedProduct: BVRecommendedProduct! { - @IBOutlet weak var productName : UILabel! - @IBOutlet weak var price : UILabel! - @IBOutlet weak var numReview : UILabel! - @IBOutlet weak var rating : UILabel! - @IBOutlet weak var productImageView : UIImageView! - @IBOutlet weak var starRating : HCSStarRatingView! - - override var bvRecommendedProduct: BVRecommendedProduct! { - - didSet { - - print("Prod: " + bvRecommendedProduct.debugDescription) - - let imageUrl = NSURL(string: bvRecommendedProduct.imageURL) - self.productName.text = bvRecommendedProduct.productName - self.rating.text = "\(bvRecommendedProduct.averageRating.floatValue)" - self.numReview.text = "(\(bvRecommendedProduct.numReviews.intValue) reviews)" - self.starRating.value = (CGFloat)(bvRecommendedProduct.averageRating.floatValue) - self.productImageView?.sd_setImage(with: imageUrl as URL!) - - } - + didSet { + + print("Prod: " + bvRecommendedProduct.debugDescription) + + guard let imageUrl = URL(string: bvRecommendedProduct.imageURL) else { + return + } + self.productName.text = bvRecommendedProduct.productName + self.rating.text = "\(bvRecommendedProduct.averageRating.floatValue)" + self.numReview.text = "(\(bvRecommendedProduct.numReviews.intValue) reviews)" + self.starRating.value = (CGFloat)(bvRecommendedProduct.averageRating.floatValue) + self.productImageView?.sd_setImage(with: imageUrl) + } + } + } diff --git a/Podfile.lock b/Podfile.lock index 04186d41..8a0dffd0 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -19,9 +19,13 @@ DEPENDENCIES: - OHHTTPStubs - OHHTTPStubs/Swift +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - OHHTTPStubs + SPEC CHECKSUMS: OHHTTPStubs: 752f9b11fd810a15162d50f11c06ff94f8e012eb PODFILE CHECKSUM: 5fbe459578ff9a227794d5444bc75824410c0a86 -COCOAPODS: 1.4.0 +COCOAPODS: 1.5.2 From e1a315ca7aa282f3df48caffe66a4b54a95b3b45 Mon Sep 17 00:00:00 2001 From: Michael Van Milligan Date: Mon, 25 Jun 2018 13:30:31 -0500 Subject: [PATCH 3/4] MOB-858: Change Recommendations Analytics BVProduct ID. --- BVSDK.podspec | 2 +- BVSDK.xcodeproj/project.pbxproj | 8 +- BVSDK/BVCommon/BVSDKConstants.h | 22 +- .../BVRecommendationsLoader.m | 352 +++++++++--------- .../BVRecommendations/BVRecsAnalyticsHelper.m | 2 +- BVSDK/Support/Info.plist | 2 +- .../DisplayTests/ProfileDisplayTests.swift | 5 +- 7 files changed, 205 insertions(+), 188 deletions(-) diff --git a/BVSDK.podspec b/BVSDK.podspec index 2c72a0e9..a10e226d 100644 --- a/BVSDK.podspec +++ b/BVSDK.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.name = "BVSDK" - s.version = '7.0.0' + s.version = '7.0.1' s.homepage = 'https://developer.bazaarvoice.com/' s.license = { :type => 'Commercial', :text => 'See https://developer.bazaarvoice.com/API_Terms_of_Use' } s.author = { 'Bazaarvoice' => 'support@bazaarvoice.com' } diff --git a/BVSDK.xcodeproj/project.pbxproj b/BVSDK.xcodeproj/project.pbxproj index 2e0e73d1..85f04ff0 100644 --- a/BVSDK.xcodeproj/project.pbxproj +++ b/BVSDK.xcodeproj/project.pbxproj @@ -2638,7 +2638,7 @@ 87F2DAA61DAD579D00FB43F3 = { CreatedOnToolsVersion = 8.0; LastSwiftMigration = 0910; - ProvisioningStyle = Automatic; + ProvisioningStyle = Manual; }; 87F2DAAF1DAD579D00FB43F3 = { CreatedOnToolsVersion = 8.0; @@ -3204,7 +3204,9 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -3220,6 +3222,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.bvsdk; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -3230,7 +3233,9 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -3246,6 +3251,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.bazaarvoice.bvsdk; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; }; name = Release; diff --git a/BVSDK/BVCommon/BVSDKConstants.h b/BVSDK/BVCommon/BVSDKConstants.h index 4f925454..edc84bd8 100644 --- a/BVSDK/BVCommon/BVSDKConstants.h +++ b/BVSDK/BVCommon/BVSDKConstants.h @@ -11,24 +11,24 @@ /// Provides the master version of the SDK. -#define BV_SDK_VERSION @"7.0.0" +#define BV_SDK_VERSION @"7.0.1" /// Conversation SDK Version #define SDK_HEADER_NAME @"X-UA-BV-SDK" -#define SDK_HEADER_VALUE @"IOS_SDK_V700" +#define SDK_HEADER_VALUE @"IOS_SDK_V701" /// Error domain for NSError results, when present. #define BVErrDomain @"com.bazaarvoice.bvsdk" -#define SYSTEM_VERSION_IOS_10 \ - ([[[UIDevice currentDevice] systemVersion] \ - compare:@"10.0" \ - options:NSNumericSearch] != NSOrderedAscending) +#define SYSTEM_VERSION_IOS_10 \ + ([[[UIDevice currentDevice] systemVersion] \ + compare:@"10.0" \ + options:NSNumericSearch] != NSOrderedAscending) #define LOG_DEPRECATED_MESSAGE(message) \ - ([[BVLogger sharedLogger] \ - warning:[NSString stringWithFormat:@"%@#%@ is deprecated and will be " \ - @"removed in a future release", \ - NSStringFromClass([self class]), \ - message]]); + ([[BVLogger sharedLogger] \ + warning:[NSString stringWithFormat:@"%@#%@ is deprecated and will be " \ + @"removed in a future release", \ + NSStringFromClass([self class]), \ + message]]); #endif /* BVSDKConstants_h */ diff --git a/BVSDK/BVRecommendations/BVRecommendationsLoader.m b/BVSDK/BVRecommendations/BVRecommendationsLoader.m index 90357c86..15b01ca6 100644 --- a/BVSDK/BVRecommendations/BVRecommendationsLoader.m +++ b/BVSDK/BVRecommendations/BVRecommendationsLoader.m @@ -20,239 +20,249 @@ @interface BVRecommendationsLoader () @implementation BVRecommendationsLoader + (void)purgeRecommendationsCache { - [[BVShopperProfileRequestCache sharedCache] removeAllCachedResponses]; + [[BVShopperProfileRequestCache sharedCache] removeAllCachedResponses]; } - (instancetype)init { - return ((self = [super init])); + return ((self = [super init])); } - (void)loadRequest:(BVRecommendationsRequest *)request completionHandler:(recommendationsCompletionHandler)completionHandler errorHandler:(recommendationsErrorHandler)errorHandler { - // check if SDK is properly configured - // if not, hit the error handler - if (![self isSDKValid]) { - [self errorOnMainThread:[self invalidSDKError] handler:errorHandler]; - return; - } + // check if SDK is properly configured + // if not, hit the error handler + if (![self isSDKValid]) { + [self errorOnMainThread:[self invalidSDKError] handler:errorHandler]; + return; + } + + BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; + NSString *client = sdkMgr.configuration.clientId; + NSString *apiRoot = sdkMgr.urlRootShopperAdvertising; + NSString *apiKey = sdkMgr.configuration.apiKeyShopperAdvertising; + + // check that `apiKeyShopperAdvertising` is valid. Will fail only in debug + // mode. + NSAssert(apiKey && 0 < apiKey.length, + @"You must supply apiKeyShopperAdvertising in the " + @"BVSDKManager before using the Bazaarvoice SDK."); + + // Cool, clientId and passKey are valid. + + BVShopperProfileRequestCache *cache = + [BVShopperProfileRequestCache sharedCache]; + + NSUInteger limit = request.limit; + NSString *productId = request.productId; + NSString *categoryId = request.categoryId; + + NSString *idfaString = [self getIdfaString]; + + if (limit <= 0 || limit > 50) { + limit = 20; // default limit + } + + NSString *sdkVersionParam = [NSString + stringWithFormat:@"%@=%@", @"_bvIosSdkVersion", BV_SDK_VERSION]; + + NSString *idParam = + [NSString stringWithFormat:@"magpie_idfa_%@", idfaString]; + + NSString *filterTypes = @"interests,brands,recommendations,reviews"; + + NSString *endPoint = [NSString + stringWithFormat:@"%@/recommendations/" + @"%@?%@&passKey=%@&include=%@&limit=%lu&client=%@", + apiRoot, idParam, sdkVersionParam, apiKey, filterTypes, + (unsigned long)limit, client]; + + if (productId != nil) { + endPoint = [endPoint + stringByAppendingString:[NSString + stringWithFormat:@"&product=%@/%@", + client, productId]]; + } + + if (categoryId != nil) { + endPoint = [endPoint + stringByAppendingString:[NSString stringWithFormat:@"&category=%@", + categoryId]]; + } - BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; - NSString *client = sdkMgr.configuration.clientId; - NSString *apiRoot = sdkMgr.urlRootShopperAdvertising; - NSString *apiKey = sdkMgr.configuration.apiKeyShopperAdvertising; - - // check that `apiKeyShopperAdvertising` is valid. Will fail only in debug - // mode. - NSAssert(apiKey && 0 < apiKey.length, - @"You must supply apiKeyShopperAdvertising in the " - @"BVSDKManager before using the Bazaarvoice SDK."); - - // Cool, clientId and passKey are valid. - - BVShopperProfileRequestCache *cache = - [BVShopperProfileRequestCache sharedCache]; - - NSUInteger limit = request.limit; - NSString *productId = request.productId; - NSString *categoryId = request.categoryId; - - NSString *idfaString = [self getIdfaString]; - - if (limit <= 0 || limit > 50) { - limit = 20; // default limit - } - - NSString *idParam = [NSString stringWithFormat:@"magpie_idfa_%@", idfaString]; - - NSString *filterTypes = @"interests,brands,recommendations,reviews"; - - NSString *endPoint = [NSString - stringWithFormat: - @"%@/recommendations/%@?passKey=%@&include=%@&limit=%lu&client=%@", - apiRoot, idParam, apiKey, filterTypes, (unsigned long)limit, client]; - - if (productId != nil) { - endPoint = [endPoint - stringByAppendingString:[NSString stringWithFormat:@"&product=%@/%@", - client, productId]]; - } - - if (categoryId != nil) { - endPoint = [endPoint - stringByAppendingString:[NSString stringWithFormat:@"&category=%@", - categoryId]]; - } - - [[BVLogger sharedLogger] - verbose:[NSString stringWithFormat:@"GET: %@", endPoint]]; + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"GET: %@", endPoint]]; - NSURLRequest *networkRequest = - [NSURLRequest requestWithURL:[NSURL URLWithString:endPoint]]; + NSURLRequest *networkRequest = + [NSURLRequest requestWithURL:[NSURL URLWithString:endPoint]]; - NSCachedURLResponse *cachedResp = - [cache cachedResponseForRequest:networkRequest]; + NSCachedURLResponse *cachedResp = + [cache cachedResponseForRequest:networkRequest]; - NSURLSession *session = nil; - id sessionDelegate = - [BVSDKManager sharedManager].urlSessionDelegate; - if (sessionDelegate && - [sessionDelegate respondsToSelector:@selector(URLSessionForBVObject:)]) { - session = [sessionDelegate URLSessionForBVObject:self]; - } + NSURLSession *session = nil; + id sessionDelegate = + [BVSDKManager sharedManager].urlSessionDelegate; + if (sessionDelegate && + [sessionDelegate + respondsToSelector:@selector(URLSessionForBVObject:)]) { + session = [sessionDelegate URLSessionForBVObject:self]; + } - if (cachedResp) { - NSDictionary *responseDict = - [NSJSONSerialization JSONObjectWithData:cachedResp.data - options:kNilOptions - error:nil]; + if (cachedResp) { + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:cachedResp.data + options:kNilOptions + error:nil]; - BVShopperProfile *profile = - [[BVShopperProfile alloc] initWithDictionary:responseDict]; + BVShopperProfile *profile = + [[BVShopperProfile alloc] initWithDictionary:responseDict]; - [cache printCacheSize]; + [cache printCacheSize]; - [[BVLogger sharedLogger] - verbose:[NSString - stringWithFormat:@"CACHED RESPONSE: %@", responseDict]]; + [[BVLogger sharedLogger] + verbose:[NSString + stringWithFormat:@"CACHED RESPONSE: %@", responseDict]]; - [self completionOnMainThread:profile.recommendations - handler:completionHandler]; + [self completionOnMainThread:profile.recommendations + handler:completionHandler]; - return; - } + return; + } - session = session ?: [BVNetworkingManager sharedManager].bvNetworkingSession; + session = + session ?: [BVNetworkingManager sharedManager].bvNetworkingSession; - NSURLSessionDataTask *task = [session - dataTaskWithRequest:networkRequest - completionHandler:^(NSData *data, NSURLResponse *response, - NSError *error) { + NSURLSessionDataTask *task = [session + dataTaskWithRequest:networkRequest + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *error) { - NSHTTPURLResponse *urlResp = (NSHTTPURLResponse *)response; + NSHTTPURLResponse *urlResp = (NSHTTPURLResponse *)response; - if ((!error && urlResp.statusCode < 300) && data != nil) { - NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response; + if ((!error && urlResp.statusCode < 300) && data != nil) { + NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response; - NSError *errorJSON; - NSDictionary *responseDict = - [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&errorJSON]; + NSError *errorJSON; + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&errorJSON]; - if (!errorJSON) { - BVShopperProfile *profile = - [[BVShopperProfile alloc] initWithDictionary:responseDict]; + if (!errorJSON) { + BVShopperProfile *profile = [[BVShopperProfile alloc] + initWithDictionary:responseDict]; - [[BVLogger sharedLogger] - verbose:[NSString stringWithFormat:@"RESPONSE: (%ld): %@", + [[BVLogger sharedLogger] + verbose:[NSString + stringWithFormat:@"RESPONSE: (%ld): %@", (long)httpResp.statusCode, responseDict]]; - // Successful response, save in cache - NSCachedURLResponse *newCachedResp = - [[NSCachedURLResponse alloc] initWithResponse:response - data:data]; + // Successful response, save in cache + NSCachedURLResponse *newCachedResp = + [[NSCachedURLResponse alloc] initWithResponse:response + data:data]; - [cache storeCachedResponse:newCachedResp - forRequest:networkRequest]; + [cache storeCachedResponse:newCachedResp + forRequest:networkRequest]; - // Success! - [self completionOnMainThread:profile.recommendations - handler:completionHandler]; + // Success! + [self completionOnMainThread:profile.recommendations + handler:completionHandler]; - return; + return; - } else { - // serialization error - [self errorOnMainThread:errorJSON handler:errorHandler]; - return; - } + } else { + // serialization error + [self errorOnMainThread:errorJSON handler:errorHandler]; + return; + } - } else { - // request error - if (error) { - [self errorOnMainThread:error handler:errorHandler]; - return; } else { - NSDictionary *userInfo = - @{NSLocalizedDescriptionKey : urlResp.description}; - NSError *err = [NSError errorWithDomain:BVErrDomain - code:urlResp.statusCode - userInfo:userInfo]; - [self errorOnMainThread:err handler:errorHandler]; - return; + // request error + if (error) { + [self errorOnMainThread:error handler:errorHandler]; + return; + } else { + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : urlResp.description}; + NSError *err = [NSError errorWithDomain:BVErrDomain + code:urlResp.statusCode + userInfo:userInfo]; + [self errorOnMainThread:err handler:errorHandler]; + return; + } } - } - }]; + }]; - [task resume]; + [task resume]; - if (sessionDelegate && - [sessionDelegate respondsToSelector:@selector - (URLSessionTask:fromBVObject:withURLSession:)]) { - [sessionDelegate URLSessionTask:task - fromBVObject:self - withURLSession:session]; - } + if (sessionDelegate && + [sessionDelegate respondsToSelector:@selector(URLSessionTask: + fromBVObject: + withURLSession:)]) { + [sessionDelegate URLSessionTask:task + fromBVObject:self + withURLSession:session]; + } } - (void) completionOnMainThread:(NSArray *)recommendations handler:(recommendationsCompletionHandler)completionHandler { - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(recommendations); - }); + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(recommendations); + }); } - (void)errorOnMainThread:(NSError *)error handler:(recommendationsErrorHandler)errorHandler { - dispatch_async(dispatch_get_main_queue(), ^{ - errorHandler(error); - }); + dispatch_async(dispatch_get_main_queue(), ^{ + errorHandler(error); + }); } - (NSString *)getIdfaString { - if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) { - return [[[ASIdentifierManager sharedManager] advertisingIdentifier] - UUIDString]; - } else { - return @"nontracking"; - } + if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) { + return [[[ASIdentifierManager sharedManager] advertisingIdentifier] + UUIDString]; + } else { + return @"nontracking"; + } } - (BOOL)isSDKValid { - NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; - NSString *passKey = - [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; + NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; + NSString *passKey = + [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; - if (clientId == nil || passKey == nil || [clientId isEqualToString:@""] || - [passKey isEqualToString:@""]) { - return NO; - } + if (clientId == nil || passKey == nil || [clientId isEqualToString:@""] || + [passKey isEqualToString:@""]) { + return NO; + } - return YES; + return YES; } - (NSError *)invalidSDKError { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; - NSString *passKey = - [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; + NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; + NSString *passKey = + [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; - if ([clientId isEqualToString:@""]) { - [userInfo setValue:@"Client Id is not set." - forKey:NSLocalizedDescriptionKey]; - } + if ([clientId isEqualToString:@""]) { + [userInfo setValue:@"Client Id is not set." + forKey:NSLocalizedDescriptionKey]; + } - if ([passKey isEqualToString:@""]) { - [userInfo setValue:@"apiKeyShopperAdvertising is not set." - forKey:NSLocalizedDescriptionKey]; - } + if ([passKey isEqualToString:@""]) { + [userInfo setValue:@"apiKeyShopperAdvertising is not set." + forKey:NSLocalizedDescriptionKey]; + } - return [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; + return [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; } @end diff --git a/BVSDK/BVRecommendations/BVRecsAnalyticsHelper.m b/BVSDK/BVRecommendations/BVRecsAnalyticsHelper.m index d6fcf4ea..fcc3b1fd 100644 --- a/BVSDK/BVRecommendations/BVRecsAnalyticsHelper.m +++ b/BVSDK/BVRecommendations/BVRecsAnalyticsHelper.m @@ -15,7 +15,7 @@ @implementation BVRecsAnalyticsHelper __strong static BVRecsAnalyticsHelper *analyticsInstance = nil; -static const NSString *bvProductName = @"Recommendations"; +static const NSString *bvProductName = @"Personalization"; + (NSDictionary *)getRelavantInfoForRecommendationType: (BVRecommendedProduct *)product diff --git a/BVSDK/Support/Info.plist b/BVSDK/Support/Info.plist index 53ec2b4d..e43b5d84 100644 --- a/BVSDK/Support/Info.plist +++ b/BVSDK/Support/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 7.0.0 + 7.0.1 CFBundleVersion 1 LSApplicationCategoryType diff --git a/BVSDKTests/ConversationsTests/DisplayTests/ProfileDisplayTests.swift b/BVSDKTests/ConversationsTests/DisplayTests/ProfileDisplayTests.swift index c5341593..690e9ed4 100644 --- a/BVSDKTests/ConversationsTests/DisplayTests/ProfileDisplayTests.swift +++ b/BVSDKTests/ConversationsTests/DisplayTests/ProfileDisplayTests.swift @@ -6,6 +6,7 @@ // import XCTest +import Foundation @testable import BVSDK // Tests conforming to API description at: https://developer.bazaarvoice.com/docs/read/conversations_api/reference/latest/profiles/display @@ -116,8 +117,8 @@ class ProfileDisplayTests: XCTestCase { let profile = response.results.first! // QA Statistics - XCTAssertEqual(profile.qaStatistics?.totalAnswerCount, 33) - XCTAssertEqual(profile.qaStatistics?.totalQuestionCount, 37) + XCTAssertGreaterThanOrEqual(profile.qaStatistics?.totalAnswerCount?.intValue ?? -1, 37) + XCTAssertGreaterThanOrEqual(profile.qaStatistics?.totalQuestionCount?.intValue ?? -1, 37) XCTAssertEqual(profile.qaStatistics?.answerHelpfulVoteCount, 0) XCTAssertEqual(profile.qaStatistics?.helpfulVoteCount, 0) XCTAssertEqual(profile.qaStatistics?.answerHelpfulVoteCount, 0) From 33e0132f7242fdd1721771538daa6b8e23874474 Mon Sep 17 00:00:00 2001 From: Michael Van Milligan Date: Mon, 25 Jun 2018 13:57:06 -0500 Subject: [PATCH 4/4] Adding AppIcon to get past cocoapods error. --- .../AppIcon.appiconset/Contents.json | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 BVSDK/BVCurationsUI/SocialMediaIcons/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/BVSDK/BVCurationsUI/SocialMediaIcons/Assets.xcassets/AppIcon.appiconset/Contents.json b/BVSDK/BVCurationsUI/SocialMediaIcons/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/BVSDK/BVCurationsUI/SocialMediaIcons/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file