<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Conner</title>
    <description>The latest articles on DEV Community by Conner (@conner).</description>
    <link>https://dev.to/conner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F585874%2F5bfb155b-31b1-4472-9ab6-cfe2b91fb53a.jpeg</url>
      <title>DEV Community: Conner</title>
      <link>https://dev.to/conner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/conner"/>
    <language>en</language>
    <item>
      <title>IP spoofing: Theory and implementation</title>
      <dc:creator>Conner</dc:creator>
      <pubDate>Mon, 29 Mar 2021 09:40:08 +0000</pubDate>
      <link>https://dev.to/conner/ip-spoofing-theory-and-implementation-ep6</link>
      <guid>https://dev.to/conner/ip-spoofing-theory-and-implementation-ep6</guid>
      <description>&lt;h1&gt;
  
  
  Theory
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Background
&lt;/h3&gt;

&lt;p&gt;When using the internet your devices are assigned a unique 32 bit (IPv4) or 128 bit (IPv6) identifier called an IP address.&lt;br&gt;
You can find the IP address assigned to your network by clicking &lt;a href="https://whatismyip.com"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Think of your IP address as the &lt;strong&gt;from:&lt;/strong&gt; field one would write on an envelope, and the destination IP address as the &lt;strong&gt;to:&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you wanted to pretend to be someone else when sending mail, you can easily erase your address and write a different one, this concept is referred to in this article as &lt;em&gt;spoofing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As it turns out, the internet works in the same way, on every chunk of data your network sends out conforming to the IP protocol, a header precedes your message containing the &lt;em&gt;from&lt;/em&gt; and &lt;em&gt;to&lt;/em&gt; fields of the internet. This header is written by your device.&lt;/p&gt;

&lt;p&gt;The internet has its foundations in the IP protocol, but most traffic uses TCP/IP or UDP/IP protocol stacks, meaning a TCP or UDP header is included in our message. TCP is important in verifying the source IP of a message, and doesn't enable sustained IP spoofing.&lt;/p&gt;
&lt;h3&gt;
  
  
  Research
&lt;/h3&gt;

&lt;p&gt;The IP suite is quite complex and has a long history, but overall it defines a set of rules for a software implementation, called the IP stack.&lt;/p&gt;

&lt;p&gt;The Internet Protocol itself is used to relay datagrams consisting of a header and data payload. The header section consists of the source IP address, destination IP address, and other relevant fields needed to route and verify the packet. The data payload consists of the data being sent.&lt;/p&gt;

&lt;p&gt;In order to view the technical information on what an IPv4 header contains, take a look at the &lt;a href="https://www.rfc-editor.org/ien/ien54.pdf"&gt;Internetwork Protocol Specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the specification, an IP header is defined on page 15 as the following:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hsW-vp74--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhidfg1us7i2tzsjadhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hsW-vp74--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhidfg1us7i2tzsjadhr.png" alt="Header Contents"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a bit strange to decipher but each &lt;strong&gt;-&lt;/strong&gt; character denotes 1 bit, and each field is read from starting &lt;strong&gt;!&lt;/strong&gt; to ending &lt;strong&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, Fragment Offset is 13 bits, and there are 13 &lt;strong&gt;-&lt;/strong&gt; characters from its preceding &lt;strong&gt;!&lt;/strong&gt; and subsequent &lt;strong&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On page 16 of the specification, we find a few interesting definitions:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EbsMZzv7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dh7r2b260wiz11g0s1yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EbsMZzv7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dh7r2b260wiz11g0s1yg.png" alt="Source Address Field Definition"&gt;&lt;/a&gt;&lt;br&gt;
The source address field definition, which we intend to change, specifically the last 3 octets (24 bits).&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lqsnp3qE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh7rak63ctxive5owf9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lqsnp3qE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh7rak63ctxive5owf9m.png" alt="Checksum Header Definition"&gt;&lt;/a&gt;&lt;br&gt;
And the checksum field description, which we will also have to change in order for our datagram to be considered valid.&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation Plans
&lt;/h3&gt;

&lt;p&gt;Before we get started with the implementation, something important to note is that we cannot feasibly spoof an IP with a TCP/IP stack. This is not possible because TCP connections begin with a 3 way handshake between sender and receiver, and who will the receiver of our message try to complete the handshake with? Our spoofed IP and not our actual one.&lt;/p&gt;

&lt;p&gt;However we can use a spoofed IP in the UDP/IP stack, the UDP protocol does not care about a handshake, and will accept communication in a single direction, sender to receiver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Spoofing an IP does not actually require TCP or UDP as a higher level protocol. But we intend to use UDP because it supports port specification, making it easier to test our program.&lt;/p&gt;

&lt;p&gt;Now that we've done our research, we have the information needed to determine what we want to change. But how will we modify an IP header? &lt;/p&gt;

&lt;p&gt;Before we go searching for information on how an IP header can be modified in code, let's design our program.&lt;/p&gt;
&lt;h3&gt;
  
  
  Program Design
&lt;/h3&gt;

&lt;p&gt;Our program will use C, as it suits a low-level project like this. It will accept a single argument, the destination address.&lt;br&gt;
All other configuration can be done in code and recompiled.&lt;br&gt;
&lt;code&gt;$ ./ipspoofer 127.0.0.1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It will use the UDP protocol in order to send data, since a 3 way handshake cannot be established under TCP IP spoofing.&lt;/p&gt;
&lt;h3&gt;
  
  
  API Research
&lt;/h3&gt;

&lt;p&gt;During research the following libraries were found that allow us to modify IP headers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.tcpdump.org/"&gt;libpcap&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
A packet capture library intended for the analysis of incoming and outgoing TCP/IP packets on a device, most likely a network device located in &lt;code&gt;/dev/&lt;/code&gt;. However it doesn't seem to be intended for packet modification. The windows port is winpcap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/libnet/libnet"&gt;libnet&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Libnet describes itself as "An API created to help with the &lt;strong&gt;contruction&lt;/strong&gt; and injection of network packets [...]&lt;br&gt;
Libnet includes packet creation at the IP layer and at the link layer as well as a host of supplementary and complementary functionality".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;raw sockets (unix kernel API)&lt;/strong&gt;&lt;br&gt;
If you've ever invoked the &lt;a href="https://linux.die.net/man/2/socket"&gt;socket()&lt;/a&gt; function using the socket library for a linux system, you've probably either used &lt;code&gt;SOCK_STREAM&lt;/code&gt; or &lt;code&gt;SOCK_DGRAM&lt;/code&gt; as they correspond to TCP and UDP respectively.&lt;br&gt;
There are however a few more socket types, the one to be used in our code is &lt;code&gt;SOCK_RAW&lt;/code&gt;. This socket type allows us to construct our &lt;code&gt;protocol&lt;/code&gt; header manually, for protocol we will use &lt;code&gt;IPPROTO_RAW&lt;/code&gt;. &lt;br&gt;
This means we will construct the IP and UDP headers and supply them to the kernel.&lt;/p&gt;

&lt;p&gt;To summarize the design, we are writing C code to create a program that will construct a UDP/IP datagram, then forward it to its destination IP. We will use raw sockets and syscalls provided by linux kernel as they are much simpler than utilizing a 3rd party library.&lt;/p&gt;

&lt;p&gt;The caveat to an implementation using raw sockets is that we will have to create our IP and UDP headers manually, as opposed to simply modifying and injecting captured packets, as can be done with libpcap and libnet. &lt;/p&gt;
&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;Let's get started writing code, the full source for this project is available &lt;a href="https://github.com/XNUConner/IPSpoofer"&gt;here&lt;/a&gt;. But we will go over the main bits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Code was tested on Linux, FreeBSD, and MacOS. It compiles on all 3 platforms but does not send datagrams on MacOS. &lt;/p&gt;

&lt;p&gt;After including the necessary headers, we will create our socket with the following arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AF_INET&lt;/code&gt; means we will be using IPv4.&lt;br&gt;
&lt;code&gt;SOCK_RAW&lt;/code&gt; means we will be constructing our protocol header.&lt;br&gt;
&lt;code&gt;IPPROTO_RAW&lt;/code&gt; means we supply all headers from the IP protocol to the higher level protocols we are using.&lt;/p&gt;

&lt;p&gt;We will use 2 predefined structs for our headers, &lt;code&gt;ip_header&lt;/code&gt; and &lt;code&gt;udp_header&lt;/code&gt;, included from &lt;code&gt;netinet/ip.h&lt;/code&gt; and &lt;code&gt;netinet/udp.h&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// IP header
struct ip ip_header;
ip_header.ip_hl = sizeof(struct ip) / 4; // Header length is size of header in 32bit words, always 5.
ip_header.ip_v = 4;                      // IPv4
ip_header.ip_tos = 0;                    // Type of service, See RFC for explanation.
ip_header.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + datalen);
ip_header.ip_id = 0;                     // Can be incremented each time by setting datagram[4] to an unsigned short.
ip_header.ip_off = 0;                    // Fragment offset, see RFC for explanation.
ip_header.ip_ttl = IPDEFTTL;             // Time to live, default 60.
ip_header.ip_p = IPPROTO_UDP;            // Using UDP protocol.
ip_header.ip_sum = 0;                    // Checksum, set by kernel.

// Source IP
struct in_addr src_ip;
src_ip.s_addr = inet_addr(src_addr);
ip_header.ip_src = src_ip;

// Destination IP
struct in_addr dst_ip;
dst_ip.s_addr = inet_addr(dest_addr);
ip_header.ip_dst = dst_ip;

// UDP Header
struct udphdr udp_header;
udp_header.uh_sport = htons(src_port);                          // Source port.
udp_header.uh_dport = htons(dest_port);                         // Destination port.
udp_header.uh_ulen = htons(sizeof(struct udphdr) + datalen);    // Length of data + udp header length.
udp_header.uh_sum = 0;                                          // udp checksum (not set by us or kernel).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To construct the datagram to be sent, we need to fill a buffer with data, starting with the IP header, then UDP header, and finally the data payload.&lt;br&gt;
We can copy a struct into memory with &lt;code&gt;memcpy()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Construct datagram
int datagram_size = sizeof(struct ip) + sizeof(struct udphdr) + datalen;
unsigned char datagram[datagram_size];
memcpy(datagram, &amp;amp;ip_header, sizeof(struct ip));
memcpy(datagram+sizeof(struct ip), &amp;amp;udp_header, sizeof(struct udphdr));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we fill out a &lt;code&gt;struct sockaddr_in&lt;/code&gt; to hold info about where we are sending this datagram, and send it repeatedly with &lt;code&gt;sendto()&lt;/code&gt; until SIGTERM is sent via ctrl+c.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sendto() destination
struct sockaddr_in destaddr;
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(dest_port);
destaddr.sin_addr.s_addr = inet_addr(dest_addr);

// Send until SIGTERM
for(;;) {
    sendto(s, datagram, datagram_size, 0,(struct sockaddr*)&amp;amp;destaddr, sizeof(destaddr));
    sleep(1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thats an overall gist of the code, there's more needed to make this work, and full explanation is inside the source code available at the github link at the top of this section.&lt;/p&gt;

&lt;h2&gt;
  
  
  View from the Receiver
&lt;/h2&gt;

&lt;p&gt;To verify that our datagrams are being sent, we can use netcat to listen on an open port and display incoming traffic.&lt;br&gt;
&lt;code&gt;$ nc -lvu &amp;lt;port&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;-l&lt;/code&gt; - Listen (run as a receiver).&lt;br&gt;
&lt;code&gt;-v&lt;/code&gt; - Verbose (will show incoming connection IP).&lt;br&gt;
&lt;code&gt;-u&lt;/code&gt; - Use UDP.&lt;/p&gt;

&lt;p&gt;Run our program and we should see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connection from 1.2.3.4 2000 received!
...random data...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; MacOS has no verbose output, you will only see the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wireshark Analysis
&lt;/h2&gt;

&lt;p&gt;In order to verify traffic is being sent by our device, &lt;a href="https://wireshark.org"&gt;Wireshark&lt;/a&gt; can be used to capture and analyze outgoing packets.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mkeJlO_I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuidsxcasyshbacsp7ay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mkeJlO_I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuidsxcasyshbacsp7ay.png" alt="UDP Packets sent seen in wireshark"&gt;&lt;/a&gt;&lt;br&gt;
If all is well, wireshark should look like this, a blue UDP protocol packet with the source being our spoofed address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GgqMnzoB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g3myjbyq6z8urorroxc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GgqMnzoB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g3myjbyq6z8urorroxc2.png" alt="Packet headers and data seen in wireshark"&gt;&lt;/a&gt;&lt;br&gt;
Inspecting the packet, we can see the contents above.&lt;br&gt;
Everything looks good, source IP, destination IP, and ports are set. Checksum was set by kernel and is verified.&lt;/p&gt;

&lt;h1&gt;
  
  
  Resolving Bugs
&lt;/h1&gt;

&lt;h3&gt;
  
  
  MTU
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;packet_size&lt;/code&gt; is set larger than the MTU, the datagrams will not be sent, and will not appear in wireshark.&lt;/p&gt;

&lt;p&gt;A network device MTU refers to its &lt;a href="https://en.wikipedia.org/wiki/Maximum_transmission_unit"&gt;Maximum Transmission Unit&lt;/a&gt;, which is the maximum amount of data that can be sent in one IP datagram without fragmenting. Typically an MTU is 1500.&lt;/p&gt;

&lt;p&gt;You can find your network device MTU with &lt;code&gt;ifconfig | grep mtu&lt;/code&gt; on MacOS and FreeBSD, and with &lt;code&gt;ip addr | grep mtu&lt;/code&gt; on linux.&lt;/p&gt;

&lt;p&gt;You can also set the MTU of a device with:&lt;br&gt;
&lt;code&gt;ip link set dev &amp;lt;interface&amp;gt; mtu &amp;lt;size&amp;gt;&lt;/code&gt; (Linux)&lt;br&gt;
&lt;code&gt;ifconfig &amp;lt;interface&amp;gt; mtu &amp;lt;size&amp;gt;&lt;/code&gt; (FreeBSD, MacOS)&lt;/p&gt;

&lt;h3&gt;
  
  
  MacOS
&lt;/h3&gt;

&lt;p&gt;This code does not work on MacOS, Wireshark analysis implies the kernel is overwriting our source IP and removing our UDP header. The MacOS kernel also does not calculate our checksum for us, which may be the reasoning behind this.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>showdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Fetching images by location in an iOS app</title>
      <dc:creator>Conner</dc:creator>
      <pubDate>Thu, 04 Mar 2021 02:44:05 +0000</pubDate>
      <link>https://dev.to/conner/fetching-images-by-location-in-an-ios-app-3mn5</link>
      <guid>https://dev.to/conner/fetching-images-by-location-in-an-ios-app-3mn5</guid>
      <description>&lt;p&gt;&lt;strong&gt;This article was written as a requirement for Lambda School's Curriculum&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About 4 weeks ago,&lt;/strong&gt; I began the final section of my part-time experience at Lambda School, Lambda Labs. &lt;/p&gt;

&lt;p&gt;Student developers are split into teams and collaborate to build a project that meets all release requirements. The first week is spent planning your approach to the project, and designing the app UI using &lt;a href="https://whimsical.com"&gt;Whimsical&lt;/a&gt;, a UI creation collaboration tool.&lt;/p&gt;

&lt;p&gt;Our team was given a deadline of 4 weeks to create &lt;a href="https://github.com/Lambda-School-Labs/cityspire-c-ios"&gt;CitySpire&lt;/a&gt;, a city-searching app utilizing a data science backend to display city data to web and iOS frontends. Created to help users find the perfect city for their needs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Finding Cityscapes to use
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;This article discusses&lt;/strong&gt; how I handled a technical challenge, integrating the &lt;a href="https://developers.google.com/places/web-service/overview"&gt;Google Places API&lt;/a&gt; into our iOS app, adding a crucial UI improvement to our app, changing all city images from a placeholder icon to pictures of their actual locations.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8beUJRyz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2qlq6m3wqtmbwnfu6f0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8beUJRyz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2qlq6m3wqtmbwnfu6f0.png" alt="City icon pointing to city picture"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Acquire an API key
&lt;/h2&gt;

&lt;p&gt;The first step in setting up our app to use the API is to acquire a valid API key through the &lt;a href="https://console.developers.google.com"&gt;Google Developer Console&lt;/a&gt;. This is required by Google for billing and analytics. &lt;br&gt;
No need to worry though, Google's API services are free for 90 days and won't automatically charge our account once the trial ends.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lets sign in to the &lt;a href="https://console.developers.google.com/cloud-resource-manager"&gt;Google Cloud Resource Manager&lt;/a&gt;  and add our project.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--52UjLxVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcxj91wi8xhvzy84ytvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--52UjLxVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcxj91wi8xhvzy84ytvf.png" alt="Create project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CTUi0IUm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfq928lfwtg7wdh54ax2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CTUi0IUm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfq928lfwtg7wdh54ax2.png" alt="New project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;With our project created, lets enable API services.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aA9vv_SM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lrrvkztl8d1zsjso1m25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aA9vv_SM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lrrvkztl8d1zsjso1m25.png" alt="API dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x3xBvg5X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tv4z1ofea0o4locia7n4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x3xBvg5X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tv4z1ofea0o4locia7n4.png" alt="Enable API"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Locate the Places API and enable it.&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k3Y1ENjJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suova1oz8m0h1lmp1clz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k3Y1ENjJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suova1oz8m0h1lmp1clz.png" alt="Enable places"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5:
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Generate an API key.&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---4SIu48Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzuc3bssh1oit4a0at21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---4SIu48Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzuc3bssh1oit4a0at21.png" alt="Credentials hamburger menu"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4aVwTTBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xauimabplnlnpf9h8a62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4aVwTTBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xauimabplnlnpf9h8a62.png" alt="Create credentials"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create Credentials and select API Key&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e9P68crO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mkvj6ea66a8jpucm3kw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e9P68crO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mkvj6ea66a8jpucm3kw.png" alt="API key created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 6:
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;We must enable billing in order for our API key to be valid, even though Google offers a free trial.&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7ZmLfnvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6l29iuwabh38p0nxnsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7ZmLfnvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6l29iuwabh38p0nxnsp.png" alt="Enable billing"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fill out the credit/debit card information, billing address, etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the API enabled and our key obtained and valid, it's time to request images from the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the API
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developers.google.com/places/web-service/search"&gt;Google Places API documentation&lt;/a&gt; is very clear and concise, the first thing we need to create is our &lt;em&gt;Find Place request&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Find Place request takes a text input and returns a place. The input can be any kind of Places text data, such as a name, address, or phone number. The request must be a string. A Find Place request using non-string data such as a lat/lng coordinate or plus code generates an error.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are using this API to request data regarding cities, so passing the name and state of a city will suffice. &lt;br&gt;
Example: &lt;code&gt;input="San Francisco, California"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;From the documentation, we know the endpoint URL is located at &lt;code&gt;https://maps.googleapis.com/maps/api/place/findplacefromtext/json&lt;/code&gt;, and that we are sending a POST &lt;strong&gt;request&lt;/strong&gt;, setting URL parameters and receiving a &lt;strong&gt;response&lt;/strong&gt; in JSON format.&lt;/p&gt;

&lt;p&gt;The parameters our app sets to request a city are:&lt;br&gt;
&lt;code&gt;input=CITY, STATE&lt;/code&gt;&lt;br&gt;
&lt;code&gt;key=YOUR_API_KEY&lt;/code&gt;&lt;br&gt;
&lt;code&gt;inputtype=textquery&lt;/code&gt;&lt;br&gt;
&lt;code&gt;fields=name,photos&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All in all, our request for San Francisco, California looks like this:&lt;br&gt;
&lt;code&gt;https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input=San%20Francisco,%20California&amp;amp;key=YOUR_API_KEY&amp;amp;inputtype=textquery&amp;amp;fields=name,photos&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With a JSON response containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An array of &lt;code&gt;candidates&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;status&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;code&gt;candidates&lt;/code&gt; object contains:&lt;br&gt;
&lt;code&gt;name&lt;/code&gt; - &lt;em&gt;The name of the location&lt;/em&gt;&lt;br&gt;
An array of &lt;code&gt;photos&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;photos&lt;/code&gt; object contains:&lt;br&gt;
&lt;code&gt;height&lt;/code&gt;&lt;br&gt;
&lt;code&gt;width&lt;/code&gt;&lt;br&gt;
An array of &lt;code&gt;html_attributions&lt;/code&gt; - &lt;em&gt;Credits to those who provided the photos&lt;/em&gt;&lt;br&gt;
&lt;code&gt;photo_reference&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strangely&lt;/strong&gt;, a &lt;code&gt;photos&lt;/code&gt; object does not contain any actual photos, but it does provide us with the ability to request them via the &lt;code&gt;photo_reference&lt;/code&gt; property, a base64 encoded string.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Obtaining the image
&lt;/h2&gt;

&lt;p&gt;In order to request an image via &lt;code&gt;photo_reference&lt;/code&gt;, a new endpoint must be used:&lt;br&gt;
&lt;code&gt;https://maps.googleapis.com/maps/api/place/photo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;with the following required HTTP parameters:&lt;br&gt;
&lt;code&gt;photoreference=YOUR_PHOTO_REFERENCE&lt;/code&gt;&lt;br&gt;
&lt;code&gt;key=YOUR_API_KEY&lt;/code&gt;&lt;br&gt;
&lt;code&gt;maxwidth=YOUR_MAX_WIDTH&lt;/code&gt;&lt;br&gt;
&lt;code&gt;maxheight=YOUR_MAX_HEIGHT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All in all, our GET request should look something like:&lt;br&gt;
&lt;code&gt;https://maps.googleapis.com/maps/api/place/photo?key=API_KEY&amp;amp;photorefernce=PHOTO_REFERENCE&amp;amp;maxwidth=1200&amp;amp;maxheight=800&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And &lt;em&gt;finally&lt;/em&gt; the response:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XKlId8UG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mjfvwngcdeyx5ztben9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XKlId8UG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mjfvwngcdeyx5ztben9.jpeg" alt="Golden gate bridge"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The End Result
&lt;/h1&gt;

&lt;p&gt;After integrating this knowledge into our app using &lt;code&gt;URLSession&lt;/code&gt; to make REST API requests, great results are seen in the collection view of our app. Google Places has brought us a picture for every city in our list!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jNoREczq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iu6dkqdrcawy9tvwnb7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jNoREczq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iu6dkqdrcawy9tvwnb7c.png" alt="Resulting app screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the end of our project it had good amount of features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MapKit section where user can search for a city, and display it on the map.&lt;/li&gt;
&lt;li&gt;Overlay view to display city information in the map.&lt;/li&gt;
&lt;li&gt;Google Places integration to fetch images for each city.&lt;/li&gt;
&lt;li&gt;Favoriting cities added to the user's profile.&lt;/li&gt;
&lt;li&gt;Collection view alongside the map, allowing users to search and filter through cities outside of Mapkit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Future Challenges
&lt;/h1&gt;

&lt;p&gt;For the continuation of work on this project, a UI overhaul is definitely needed. Our app has mismatching fonts, a default apple color scheme, and not much color besides white and black.&lt;/p&gt;

&lt;p&gt;There are still some technical challenges in the form of bugs too, the image caching was written hastily near a our deadline, and it can unexpectedly display the wrong photo for a cell in the collection view.&lt;/p&gt;

&lt;p&gt;API requests for the Mapkit view controller are also made even if the cache contains and image for the requested city, because of the speed in which this image fetch was implemented, it was deemed unimportant to check the cache beforehand.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reflections
&lt;/h1&gt;

&lt;p&gt;This project provided a lot of long term experience about planning, making design choices, and brainstorming about feature implementations with others in cross-platform teams.&lt;/p&gt;

&lt;p&gt;I thoroughly enjoyed the experience and found that I and others worked very well in this environment, I received a lot of great feedback on my Github pull requests and slack messages. This experience leaves me looking forward to working within large teams to create amazing products in the future.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
