#DataRequests
Explore tagged Tumblr posts
Text
So we all want to get rid of Tumblr Live, yeah?
Wel, I have an idea!
I've seen a lot of posts complaining to @staff and by all means, keep complaining!
The thing is, Tumblr Live is powered by an external company: The Meet Group, Inc. They are the ones we should be trying to get rid of.
This company wants to make money. So all we need to do, is cost them money instead.
GDPR law means that, If you live in the EU, you have a lot of rights to acces or view any personal data The Meet Group has collected on you. Since requests to exercise these rights are rare, most companies (including The Meet Group), don't seem to have automatic systems in place to deal with these GDPR requests. They have one or more human persons that they pay to manually handle these requests.
So all we need to do is exercise our rights and request our personal data! They even provide you with a handy form that wil automatically generate a request email for you!
🔗 LINK TO FORM
So go ham! set a reminder, and send a new request every couple of weeks!
Try to put some variety in your request! Ask to see your data. Next time, ask for all data to be deleted. Then, ask for some missing data to be added! consent to the Terms of Service, then withdraw your content the next time. Be creative! The more varied these requests are, the harder they wil be to process.
You can also write your own email and send it directly to: [email protected] or [email protected].
You can even call them at 844-534-1340.
By BDPR law, The Meet Group is legally required to fulfil these requests.
Just remember, DO NOT HARASS ANY EMPLOYEES! Waste their time by all means, but please BE NICE.
Non-EU residents
GDPR law only applies to EU residents, but here is the best part:
"We respond to all requests we receive from individuals wishing to exercise their data protection rights in accordance with applicable data protection laws."
- Tumblr Live Terms of Service
That sounds to me like even if you don't live in the EU, someone wil read your request, write an email response to find out if you live in the EU or not, and if you don't, explain that there is nothing they can do for you.
11 notes
·
View notes
Text
I am tired of having so many important issues raised on this page, waved away and disregarded as ‘political opinions’.
Really?
Men making up three in four suicides isn’t a political opinion, it is simply a fact.
Men dying at higher rates in every age group, is not a political opinion, it is also a fact.
Men making up one third of abuse survivors, and nine in ten homeless deaths, or the fact that boys have fallen behind girls in western education, for three decades, are not ‘political opinions’ either.
They are all facts.
And whether you pick them up, or throw them away, they will remain facts, that are each the product of decades of experience, from the world’s leading experts –
Straus, Gelles, Curnock-Cook, Neese, Dutton, Steinmetz, Stemple, and the many other diligent researchers whose work I cite, are not politicians or activists, they are researchers, and some of the very best in history.
Similarly, the Centers for Disease Control, the Office for National Statistics, or the Universities and Colleges Admissions Service, are not political bodies, but governmental organisations who simply collect and publish data.
Data. That’s it.
It is not red or blue, left or right, data doesn’t care who you are.
I understand a lot of the content here will lead to discomfort; no doubt some of the info I share can be upsetting, unpopular, and very *uncool* – but remember unpopular doesn’t mean ‘wrong’.
So how do we deal with the problem that so many of us wear ‘trendy’ political opinions to primarily look good, regardless of whether they meet objective factual truth.
What happens when such virtue signals lead to increasingly immovable opinions; and the stubborn refusal to simply look at what is beneath your nose…
And what is beneath your nose are facts.
--
Sources:
ONS: https://www.gov.uk/government/publications/supporting-male-victims/supporting-male-victims-accessible
Straus: https://www.youtube.com/watch?v=uHxQTTg_wLE
UCAS: https://tinyurl.com/5n8ht3tt
UCAS #2: https://www.ucas.com/data-and-analysis/undergraduate-statistics-and-reports/ucas-undergraduate-sector-level-end-cycle-data-resources-2020
Mary Curnock Cook: https://www.telegraph.co.uk/education/2018/11/16/boys-left-fail-school-attempts-help-earn-wrath-feminists-says/
CDC: https://wonder.cdc.gov/controller/datarequest/D158
==
“Facts are only offensive to those who have taken refuge in fantasy.” -- Michael Sherlock
#The Tin Men#domestic violence#domestic abuse#male victims#male victims of domestic abuse#violent women#education gap#early death#male deaths#religion is a mental illness
12 notes
·
View notes
Text
A Complete Facebook Customer Service Guide
Getting in touch with Facebook customer service has become increasingly frustrating and slow. This mostly happens when users don't know how to contact Meta and Facebook and state how to solve their issues.
But this can change if the users know the way to contact Facebook and resolve their problems. So today, find out the incredible ways of connecting with Facebook and fixing your problem.
Answering All the Questions About How to Contact Facebook Customer Service
Users ask various questions every day regarding Facebook and Meta. And I have collected all those questions and will answer them one by one.
So, buckle up and learn about the exclusive ways of contacting Facebook and resolving all your virtual problems.
1. Can You Tell Me How To Talk To Someone At Facebook Customer Service?
You cannot talk to Facebook’s live agent through your Facebook App. However, you can go to Facebook’s Help Center and find solutions for the problems you’re facing.
Once you log into www.facebook.com/help, you will find various factors and solutions to their problems.
For example, if you click Facebook’s Login and Password option from Help Center, you will find various articles on protecting your Facebook password.
And by reading those articles, you can protect your Facebook ID and password.
But if you still want to talk to Facebook’s live user agent, you can DM Facebook on their social media. Their live agents are present there and can chat with you and solve your issue.
2. Will a Live Person Chat With Me On Facebook?
You cannot find a live agent on Facebook if you're a regular user. However, if you own a Facebook business and advertise regularly on FB, you can access live chat support from the Facebook team.
And to access chat support, Here's all you have to do:
a. Go to Meta's ecommerce store manager on your Facebook application through your messenger.
b. After that, move to the Education section and followed by that, the Contact Chat Support option.
c. Finally, choose your reason for contacting Facebook, write your personalized message and send the chat. A liver chat agent will soon join you.
3. Does Facebook Have Any Official Email ID?
Facebook or Meta does not have any official email address. However, they have various email addresses for users and advertisers.
Here are all the email addresses that you might need if you want to communicate with Facebook agents:
● [email protected] for hacked or blocked account recovery.
● [email protected] for removed content and suspended accounts.
● [email protected] for monetary queries and status.
● [email protected] for reporting accounts that have been bullying you
● [email protected] for finding the data that Facebook has been using
● [email protected] to learn the process of advertising on Facebook
● [email protected] if someone steals your intellectual property.
● [email protected] for reporting spam accounts.
4. Is There a Way to Get In Touch With Facebook If My Account Gets Hacked?
You can contact Facebook or Meta in various ways if your account gets hacked.
First of all, you can write an email and attach proof of how you found out about the hack.
Next, send this email to [email protected].
Or you can log into your Facebook account, go to the Settings option, and then go to the help centre.
Here you will find the ways to protect your FB id from hackers and a guide to changing your Facebook ID and password.
Finally, you can slide into Facebook’s Instagram’s official id and DM them to ask about a solution.
5. Does Facebook Have a Live Support Chat?
Facebook has live chat support for business holders and advertisers. However, if you're a regular user, do you have to perish?
No! You can DM Facebook on Instagram, Twitter and other social media accounts and chat with their agents to solve all your troubles.
Here are the IDs that you must know about if you want to catch hold of a Facebook agent:
● Instagram Account on Facebook
● Twitter Account of Facebook
And if you want, you can also post a letter on Facebook. Here's the address:
Meta Platform, Inc.
FAO: Privacy Operations
1601 Willow Road
Menlo Park. CA 94025, US
Concluding Words
If you know the way, connecting with Facebook customer service is not difficult at all. Moreover, Facebook's help centre is quite proactive; therefore, you'll receive assistance if your account protection is at stake.
So, surf the internet without worry. Best of Luck!
1 note
·
View note
Text
GLOBAL EBS ANYTIME NOW, DEBT CEILING DEFAULT, WAR ESCALATION BETWEEN NATO & RUSSIA, BIDEN DRUG SHORTAGE, FLUORIDE LOWERS IQ, BELGIUM SATANIC CASTLE, TIMELINE OF EVENTS, WRAY CONTEMPT OF CONGRESS, PREP FOR NO INTERNET/POWER/ FOOD SHORTAGES/WATER, TALIBAN AMASSING EQUIP ALONG IRAN BORDER, CHINA CUT OFF PENTAGON GCR/JUDY BYINGTON UPDATE, GESARA/NESARA AND MORE.
Your support is greatly appreciated!
Here is my STRIPE link:
https://buy.stripe.com/28o9EyeHJ6wQ5KE5km
NOTE: please adjust QUANTITY to change donation amount. Also, it says “pay Truth and News” at top; that’s me. 👍
Thank you so much!
FOR CURRENT DEFCON LEVELS VISIT:
https://www.defconlevel.com/news.php
Nuremberg Code PDF:
https://media.tghn.org/medialibrary/2011/04/BMJ_No_7070_Volume_313_The_Nuremberg_Code.pd
Vaccine Vaers Data from CDC finder:
https://wonder.cdc.gov/controller/datarequest/D8
Telegram (channel ID in videos)
All Judy Byington reports: https://operationdisclosureofficial.com/2022/10/27/restored-republic-via-a-gcr-as-of-october-27-2022/
GREAT CHANNEL AND A Link to be saved by our Lord Jesus Christ:
http://www.xtremerealitycheck.com/getsaved.html
Link to AMERICAS FRONTLINE DOCTORS website for information about jab, masks, CV, research, data and LEGAL FORMS AND INFORMATION FOR EMPLOYERS, SCHOOLS, MILITARY ETC IN VIOLATION OF NUREMBERG CODE.
https://americasfrontlinedoctors.org/legal/vaccines-the-law/
Please see telegram channels posted in the video:
Richard citizen journalist
Nancy drew on Facebook & telegram
(Q) The Storm Rider
Tribunals for justice
JFKJrIsQ
Dismantling the Cabal
Whiplash347
Santa surfing
Project VERITAS (telegram & www.projectveritas.com)
The Charlie Ward show (bitchute)
Mad liberals (YouTube)
The United spot (YouTube)
@WW3INFO
Ildonaldo Trumpo
Stew peters
Red voice media
Infowars
Lars Von Retriever on youtube
App: piñata farms
Reese Report. &. Banned video
RT NEWS
WHITE HATS TELEGRAM CHANNEL
VT INTELL Courtesy of Commander Thor
MOD TELEGRAM
***ALL TIC TOK LINKS EMBEDDED IN VIDEO.
KAREN KINGSTON SUB STACK
LINK: https://karenkingston.substack.com/
COMMANDER VALIANT THOR ON TELEGRAM LINK:
https://t.me/vvt369
WE THE PEOPLE NEWS ON TELEGRAM:
https://t.me/we_the_people_NEWZ
A SPECIAL THANK YOU TO THE LIGHT BEINGS FOR ASSISTING HUMANITY IN THIS WAR AGAINST THE EVIL CABAL. WE KNOW YOU HAVE SACRIFICED MUCH AND AT TIMES YOUR LIVES HAVE BEEN LOST. WE WANT YOU TO KNOW OUR PRAYERS AND CONDOLENCES ARE WITH YOU AND THOSE LOST. WE SEND YOU LOVE, LIGHT AND PEACE AND HOPE THAT ONE DAY WE WILL MEET YOU AND BEABLE TO GIVE YOU OUR MESSAGE OF THANKS, LOVE AND APPRECIATION IN PERSON. MAY GOD BLESS YOU AND ALL BEINGS OF LOVE AND LIGHT HELPING TO RID THE WORLD OF THIS EVIL DARKNESS.
NOTE: COMMANDER THOR CAN RELAY MSGS TO THE LIGHT BEINGS AND RETRIEVE MSGS, AND IF ANYONE HAS QUESTIONS OR THOUGHTS, PLEASE ASK HIM AND HE WILL ANSWER IF HE CAN.
GENE DECODE ON RUMBLE: https://rumble.com/user/RealGeneDecode
SUDDEN DEATH EPIDEMIC TUCKER CARLSON. https://beforeitsnews.com/opinion-conservative/2023/02/tucker-carlson-today-sudden-death-epidemic-a-must-video-3655806.html
TODAYS SOURCES
https://operationdisclosureofficial.com/wp-content/uploads/2023/05/Restored-Republic-via-a-GCR-5-31-2023.docx
https://halturnerradioshow.com/index.php/en
https://realrawnews.com/2021/06/upcoming-military-tribunals-revision-3/
https://www.eyedropmedia.com/videos
0 notes
Text
Sorry for taking a moment, they really don't make it easy lol.
Here's the link for the form: https://terms.video.tumblr-live.com/privacy-rights
The form is only applicable if you're a resident of:
California
Nevada
Virginia
European Economic Area or the United Kingdom
That form won't work unless you have an email client installed on your computer or your phone.
If you access your email via a web browser, you'll have to create an email yourself. Here's how it would look for a resident of an EU country (It's different for US ones):
You send it to: [email protected]
Subject: Data Request <Country of Residence>
Body of email:
I am submitting this form on behalf of Myself
First Name: <Name> Last Name: <Surname> Country: <two letter Country Code> State: N/A Email Address: <your email, preferably the same as tumblr one> App(s): meetme,tagged,skout,growlr,partner
Rights to exercise: Delete/Erasure Personal Data Restriction of Processing Object to Processing
Tumblr why the FUCK are you showing me tumblr live? I'm not American, I thought you weren't even allowed to do this 🤢🤢🤢🤢
261 notes
·
View notes
Text
How To Contact Facebook About A Problem
Are you one of those Facebook account holders who are facing some unexpected arrival of problems while using Facebook services? Due to the occurrence of some problems with your account, you may not able to leverage the Facebook facilities. For that, you need to understand How to Contact Facebook about a Problem.
As we know that FB doesn’t offer live personal support by phone, and thus the only option to get in touch with the FB help center is; by email. Now the question is “How To Contact Facebook About A Problem” or “does FB have an email support facility”. Well, Yes! FB offers support through emails, but all you need is to perform a series of steps to reach the FB support department. So, go through the steps given below and report a problem to Facebook:
How To Email Facebook?
Step 1:
First, go to the Facebook log-in page and click “Account” from the top-right corner of the screen and a drop-down list will open for you.
Step 2:
Choose the “Help Center” option from the list. And, type a question that matches the problem you are facing with your FB account, in the “Enter a Keyword or Question” field.
Step 3:
On the next screen, look for the result that can answer your query or question. If there is no solution is available for you, type “contact Facebook” using the search bar placed at the top of the screen. You will get a list of contact options.
Step 4:
Again select a question that matches your problem and then read the piece of information provided by Facebook, for your respective issue. Then click the “Contact” or “Here” link to proceed.
Step 5:
Now, fill out the form you get, and hit the Submit form. Make sure to add the correct and active email address in the respective field. The team of FB will contact you with the same email address you have entered in the form.
List Of FB Email Addresses:
In case you are looking for a complete list of Facebook complaint/support email addresses available to reach Fb for different product help and support, we have jotted down all the currently working email addresses that can help you reach a particular FB help team:
• Financial issues (problems, refund/repayment of Facebook credit): [email protected]
• Stand for Press service / Facebook PR: [email protected]
• for problems related to advertising: [email protected]
• Problems related to intellectual property: [email protected]
• for the legal department: [email protected] et [email protected]
• to ask for your personal data: [email protected]
• if you are a designer/artist/illustrator and want to show your creations for the stickers marketplace and Facebook messenger chat: [email protected]
This is how you can reach or complain to Facebook, but you may have to wait for hours to get a revert for your complaint. So, keep the patience to get support from the Facebook team.
0 notes
Photo
Happy #Caturday! 🐈 Or is it? Several friends have had their accounts hacked😫 or cloned😖. 👿HACKING is when someone takes over an account. Users with 10K+ followers are attractive targets because they give hackers leverage to ask for a ransom. Hackers also use the follower list to contact friends & asks them for money or to click a link. Those with large followings may not be well acquainted with their followers, so their peeps can’t tell. Generally, DON’T CLICK LINKS. It can cause your account to become compromised. 👿IF YOU’VE BEEN HACKED: Watch this video🎬 by photographer @jaredquackenbush📸: bit.ly.3Jwkzy0 🌀CLONING is when an imposter copies an account. They steal the bio photo, copy the bio & some recent posts, & create a similar username. They send friend requests to existing friends, & some don’t notice the extra punctuation or spelling & accept the request. 🌀IF YOU’VE BEEN CLONED: —Change your password & update your profile picture & mention in your bio —Share on your feed & IG story the details of the imposter 📢 —Ask friends to report (“impersonating someone you know”) & block👫👭👬 —Have 1 person continue to follow the imposter to keep an eye on what’s happening👁️🗨️ ⚠️HOW TO PROTECT YOURSELF? —Change your password🤐 —Set up 2-factor authentication. 🔐 (We verify ourselves, usually through a text code) —Occasionally post a picture of yourself on your IG. 🤳 WHY? Because one of the steps to retrieve a hacked account is to shoot a selfie video. If there are no selfies on your feed, how can IG be sure it’s really you? If IG can’t tell, IG may shut down the account. —Don’t use 3rd-party apps. Hackers can use them to find a back door into your account⛔ —Periodically change your password & DON’T use the same password on multiple sites🔗 —Download your data “just in case” by going to Settings~Security~Download~ DataRequest~Download. It’ll deliver to your email address. 📧 Have you or someone you know been hacked or cloned⁉️ What happened? 🙁😢😭🥺😩 #StaySafe out there! 🙏 . . . PHOTOS: 😻Hunter & Grey🐾 make an appearance! #mainecoons #mainecoonlovers #greytabby #silvertabby #sillycats Hunter watches #TheLastKingdom . . . (at Madrona) https://www.instagram.com/p/CcHwSAPOrmW/?igshid=NGJjMDIxMWI=
1 note
·
View note
Text
What is Chainlink? | Decentralized Oracle Network | Chainlink Crypto | LINK Crypto
Lets learn about Chainlink,a decentralized network of oracles thatconnect Blockchains to real-world data. Chainlink is an Ethereum-basedproject with multi-chain support. The primary purpose of ChainLinkis to make real-world data,events, and payments information availableto smart contracts on any Blockchain. Chainlink network supports many price feeds,securing billions in value for manyleading applications and companies. However, the Chainlink Network canreliably deliver data from any API,for example viewership dataas a service to Theta.tv.
As the project evolved, Chainlinkspurpose has expanded to offer smartcontracts with highly scalable, confidential,and secure off-chain computation resources. The Chainlink network now also acts asa secure off-chain computation layer. It partly relies on Blockchains for security,yet also operates with the connectivityand scalability of off-chain systems. Sergey Nazarov, CEO of ChainlinkLabs, founded Chainlink and Chainlink Labs is responsiblefor overseeing its development. The LINK token is the currency that is usedfor all payments on the Chainlink network,as well as for staking by node operators.
Staking LINK tokens is reallyimportant for network health,because parts of the Chainlink networkoperate outside the bounds of the Blockchain. This means that nodes servingoff-chain data could act dishonestly. The staking features of Chainlinkattack this problem head-on. Dishonest nodes will not only be ignoredfrom serving future data requests,they will also be taxed on the staked tokens.
This incentivizes nodes to act honestly and servehigh quality data to enhance their reputation. As the stake and reputation increases, it becomesmore likely to be selected for datarequests and earn token rewards. Learn more about Chainlink in our videoThe Blockchains of Ready Player One. Youre watching the video channel of MarketSquare,the new homepage for the decentralized Web.
Read More: What The Next Price For ChainLink?
The post What is Chainlink? | Decentralized Oracle Network | Chainlink Crypto | LINK Crypto appeared first on Crypto Coin Guides.
via What is Chainlink? | Decentralized Oracle Network | Chainlink Crypto | LINK Crypto
0 notes
Link
A detailed analysis of the features of the most popular models will help beginners make the right choice and buy the most suitable option for themselves. http://data.houstontx.gov/datarequest/f1262b7a-8b9a-4733-9b0b-137f8b41331b
0 notes
Link
cosmetify - private label herbal cosmetics
0 notes
Link
0 notes
Text
Alamofire Tutorial: Getting Started
Update note: This tutorial has been updated to Xcode 9.3, iOS 11.3, Swift 4.1 and Alamofire 4.7.0 by Ron Kliffer. The original tutorial was written by Aaron Douglas.
Get the lowdown on Alamofire!
Alamofire is a Swift-based HTTP networking library for iOS and macOS. It provides an elegant interface on top of Apple’s Foundation networking stack that simplifies a number of common networking tasks.
Alamofire provides chainable request/response methods, JSON parameter and response serialization, authentication, and many other features.
In this Alamofire tutorial, you’ll use Alamofire to perform basic networking tasks like uploading files and requesting data from a third-party RESTful API.
Alamofire’s elegance comes from the fact it was written from the ground up in Swift and does not inherit anything from its Objective-C counterpart, AFNetworking.
You should have a conceptual understanding of HTTP networking and some exposure to Apple’s networking classes such as URLSession.
While Alamofire does obscure some implementation details, it’s good to have some background knowledge if you ever need to troubleshoot your network requests.
Getting Started
Use the Download Materials button at the top or bottom of this tutorial to download the starter project.
Note: Alamofire is normally integrated using CocoaPods. It has already been installed for you in the downloaded projects.
The app for this Alamofire tutorial is named PhotoTagger. When complete, it will let you select an image from your library (or camera if you’re running on an actual device) and upload the image to a third-party service called Imagga. This service will perform some image recognition tasks to come up with a list of tags and primary colors for the image:
This project uses CocoaPods, so open it using the PhotoTagger.xcworkspace file.
Note:To learn more about CocoaPods, check out this tutorial by Joshua Greene, published right here on the site.
Build and run the project. You’ll see the following:
Click Select Photo and choose a photo. The background image will be replaced with the image you chose.
Open Main.storyboard and you’ll see the additional screens for displaying tags and colors have been added for you. All that remains is to upload the image and fetch the tags and colors.
The Imagga API
Imagga is an image recognition Platform-as-a-Service that provides image tagging APIs for developers and businesses to build scalable, image-intensive cloud apps. You can play around with a demo of their auto-tagging service here.
You’ll need to create a free developer account with Imagga for this Alamofire tutorial. Imagga requires an authorization header in each HTTP request so only people with an account can use their services. Go to https://imagga.com/auth/signup/hacker and fill out the form. After you create your account, check out the dashboard:
Listed down in the Authorization section is a secret token you’ll use later. You’ll need to include this information with every HTTP request as a header.
Note: Make sure you copy the whole secret token, be sure to scroll over to the right and verify you copied everything.
You’ll be using Imagga’s content endpoint to upload the photos, tagging endpoint for the image recognition and colors endpoint for color identification. You can read all about the Imagga API at http://docs.imagga.com.
REST, HTTP, JSON — What’s that?
If you’re coming to this tutorial with very little experience in using third-party services over the Internet, you might be wondering what all those acronyms mean! :]
HTTP is the application protocol, or set of rules, web sites use to transfer data from the web server to your screen. You’ve seen HTTP (or HTTPS) listed in the front of every URL you type into a web browser. You might have heard of other application protocols, such as FTP, Telnet, and SSH. HTTP defines several request methods, or verbs, the client (your web browser or app) use to indicate the desired action:
GET: Retrieves data, such as a web page, but doesn’t alter any data on the server.
HEAD: Identical to GET but only sends back the headers and none of the actual data.
POST: Sends data to the server, commonly used when filling a form and clicking submit.
PUT: Sends data to the specific location provided.
DELETE: Deletes data from the specific location provided.
REST, or REpresentational State Transfer, is a set of rules for designing consistent, easy-to-use and maintainable web APIs. REST has several architecture rules that enforce things such as not persisting states across requests, making requests cacheable, and providing uniform interfaces. This makes it easy for app developers like you to integrate the API into your app, without needing to track the state of data across requests.
JSON stands for JavaScript Object Notation. It provides a straightforward, human-readable and portable mechanism for transporting data between two systems. JSON has a limited number of data types: string, boolean, array, object/dictionary, null and number. There’s no distinction between integers and decimals.
There are a few native choices for converting your objects in memory to JSON and vice-versa: the good old JSONSerialization class and the newly-added JSONEncoder and JSONDecoder classes. In addition, there are numerous third party libraries that help with handling JSON. You’ll use one of them, SwiftyJSON in this tutorial.
The combination of HTTP, REST and JSON make up a good portion of the web services available to you as a developer. Trying to understand how every little piece works can be overwhelming. Libraries like Alamofire can help reduce the complexity of working with these services, and get you up and running faster than you could without their help.
What is Alamofire Good For?
Why do you need Alamofire at all? Apple already provides URLSession and other classes for downloading content via HTTP, so why complicate things with another third party library?
The short answer is Alamofire is based on URLSession, but it frees you from writing boilerplate code which makes writing networking code much easier. You can access data on the Internet with very little effort, and your code will be much cleaner and easier to read.
There are several major functions available with Alamofire:
Alamofire.upload: Upload files with multipart, stream, file or data methods.
Alamofire.download: Download files or resume a download already in progress.
Alamofire.request: Every other HTTP request not associated with file transfers.
These Alamofire methods are global within Alamofire so you don’t have to instantiate a class to use them. There are underlying pieces to Alamofire that are classes and structs, like SessionManager, DataRequest, and DataResponse; however, you don’t need to fully understand the entire structure of Alamofire to start using it.
Here’s an example of the same networking operation with both Apple’s URLSession and Alamofire’s request function:
// With URLSession public func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) { guard let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true") else { completion(nil) return } var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0 * 1000) urlRequest.httpMethod = "GET" urlRequest.addValue("application/json", forHTTPHeaderField: "Accept") let task = urlSession.dataTask(with: urlRequest) { (data, response, error) -> Void in guard error == nil else { print("Error while fetching remote rooms: \(String(describing: error)") completion(nil) return } guard let data = data, let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { print("Nil data received from fetchAllRooms service") completion(nil) return } guard let rows = json?["rows"] as? [[String: Any]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } let rooms = rows.flatMap { roomDict in return RemoteRoom(jsonData: roomDict) } completion(rooms) } task.resume() }
Versus:
// With Alamofire func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) { guard let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true") else { completion(nil) return } Alamofire.request(url, method: .get, parameters: ["include_docs": "true"]) .validate() .responseJSON { response in guard response.result.isSuccess else { print("Error while fetching remote rooms: \(String(describing: response.result.error)") completion(nil) return } guard let value = response.result.value as? [String: Any], let rows = value["rows"] as? [[String: Any]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } let rooms = rows.flatMap { roomDict in return RemoteRoom(jsonData: roomDict) } completion(rooms) } }
You can see the required setup for Alamofire is shorter and it’s much clearer what the function does. You deserialize the response with responseJSON(options:completionHandler:) and calling validate() to verify the response status code is in the default acceptable range between 200 and 299 simplifies error condition handling.
Now the theory is out of the way, it’s time to start using Alamofire.
Uploading Files
Open ViewController.swift and add the following to the top, below import SwiftyJSON:
import Alamofire
This lets you use the functionality provided by the Alamofire module in your code, which you’ll be doing soon!
Next, go to imagePickerController(_:didFinishPickingMediaWithInfo:) and add the following to the end, right before the call to dismiss(animated:):
// 1 takePictureButton.isHidden = true progressView.progress = 0.0 progressView.isHidden = false activityIndicatorView.startAnimating() upload(image: image, progressCompletion: { [weak self] percent in // 2 self?.progressView.setProgress(percent, animated: true) }, completion: { [weak self] tags, colors in // 3 self?.takePictureButton.isHidden = false self?.progressView.isHidden = true self?.activityIndicatorView.stopAnimating() self?.tags = tags self?.colors = colors // 4 self?.performSegue(withIdentifier: "ShowResults", sender: self) })
Everything with Alamofire is asynchronous, which means you’ll update the UI in an asynchronous manner:
Hide the upload button, and show the progress view and activity view.
While the file uploads, you call the progress handler with an updated percent. This updates the progress indicator of the progress bar.
The completion handler executes when the upload finishes. This sets the controls back to their original state.
Finally the Storyboard advances to the results screen when the upload completes, successfully or not. The user interface doesn’t change based on the error condition.
Next, find upload(image:progressCompletion:completion:) at the bottom of the file. It is currently only a method stub, so give it the following implementation:
func upload(image: UIImage, progressCompletion: @escaping (_ percent: Float) -> Void, completion: @escaping (_ tags: [String]?, _ colors: [PhotoColor]?) -> Void) { // 1 guard let imageData = UIImageJPEGRepresentation(image, 0.5) else { print("Could not get JPEG representation of UIImage") return } // 2 Alamofire.upload(multipartFormData: { multipartFormData in multipartFormData.append(imageData, withName: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, to: "http://api.imagga.com/v1/content", headers: ["Authorization": "Basic xxx"], encodingCompletion: { encodingResult in }) }
Here’s what’s happening:
The image that’s being uploaded needs to be converted to a Data instance.
Here you convert the JPEG data blob (imageData) into a MIME multipart request to send to the Imagga content endpoint.
Note: Make sure to replace Basic xxx with the actual authorization header taken from the Imagga dashboard.
Next, add the following to the encodingCompletion closure:
switch encodingResult { case .success(let upload, _, _): upload.uploadProgress { progress in progressCompletion(Float(progress.fractionCompleted)) } upload.validate() upload.responseJSON { response in } case .failure(let encodingError): print(encodingError) }
This chunk of code calls the Alamofire upload function and passes in a small calculation to update the progress bar as the file uploads. It then validates the response has a status code in the default acceptable range between 200 and 299.
Note: Prior to Alamofire 4 it was not guaranteed progress callbacks were called on the main queue. Beginning with Alamofire 4, the new progress callback API is always called on the main queue.
Next, add the following code to the upload.responseJSON closure:
// 1 guard response.result.isSuccess, let value = response.result.value else { print("Error while uploading file: \(String(describing: response.result.error))") completion(nil, nil) return } // 2 let firstFileID = JSON(value)["uploaded"][0]["id"].stringValue print("Content uploaded with ID: \(firstFileID)") //3 completion(nil, nil)
Here’s a step-by-step explanation of the above code:
Check that the upload was successful, and the result has a value; if not, print the error and call the completion handler.
Using SwiftyJSON, retrieve the firstFileID from the response.
Call the completion handler to update the UI. At this point, you don’t have any downloaded tags or colors, so simply call this with no data.
Note: Every response has a Result enum with a value and type. Using automatic validation, the result is considered a success when it returns a valid HTTP Code between 200 and 299 and the Content Type is of a valid type specified in the Accept HTTP header field.
You can perform manual validation by adding .validate options as shown below:
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]) .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .response { response in // response handling code }
The UI won't show an error if you hit an error during the upload; it merely returns no tags or colors to the user. This isn't the best user experience, but it's fine for this tutorial.
Build and run your project; select an image and watch the progress bar change as the file uploads. You should see a note like the following in your console when the upload completes:
Congratulations, you've successfully uploaded a file over the Interwebs!
Retrieving Data
The next step after uploading the image to Imagga is to fetch the tags Imagga produces after it analyzes the photo.
Add the following method to the ViewController extension below upload(image:progress:completion:):
func downloadTags(contentID: String, completion: @escaping ([String]?) -> Void) { // 1 Alamofire.request("http://api.imagga.com/v1/tagging", parameters: ["content": contentID], headers: ["Authorization": "Basic xxx"]) // 2 .responseJSON { response in guard response.result.isSuccess, let value = response.result.value else { print("Error while fetching tags: \(String(describing: response.result.error))") completion(nil) return } // 3 let tags = JSON(value)["results"][0]["tags"].array?.map { json in json["tag"].stringValue } // 4 completion(tags) } }
Here's a step-by-step explanation of the above code:
Perform an HTTP GET request against the tagging endpoint, sending the URL parameter content with the ID you received after the upload. Again, be sure to replace Basic xxx with your actual authorization header.
Check that the response was successful, and the result has a value; if not, print the error and call the completion handler.
Using SwiftyJSON, retrieve the raw tags array from the response. Iterate over each dictionary object in the tags array, retrieving the value associated with the tag key.
Call the completion handler passing in the tags received from the service.
Next, go back to upload(image:progress:completion:) and replace the call to the completion handler in the success condition with the following:
self.downloadTags(contentID: firstFileID) { tags in completion(tags, nil) }
This simply sends along the tags to the completion handler.
Build and run your project; select a photo and you should see something similar to the following appear:
Pretty slick! That Imagga is one smart API. :] Next, you'll fetch the colors of the image.
Add the following method to the ViewController extension below downloadTags(contentID:completion:):
func downloadColors(contentID: String, completion: @escaping ([PhotoColor]?) -> Void) { // 1. Alamofire.request("http://api.imagga.com/v1/colors", parameters: ["content": contentID], headers: ["Authorization": "Basic xxx"]) .responseJSON { response in // 2 guard response.result.isSuccess, let value = response.result.value else { print("Error while fetching colors: \(String(describing: response.result.error))") completion(nil) return } // 3 let photoColors = JSON(value)["results"][0]["info"]["image_colors"].array?.map { json in PhotoColor(red: json["r"].intValue, green: json["g"].intValue, blue: json["b"].intValue, colorName: json["closest_palette_color"].stringValue) } // 4 completion(photoColors) } }
Taking each numbered comment in turn:
Perform an HTTP GET request against the colors endpoint, sending the URL parameter content with the ID you received after the upload. Again, be sure to replace Basic xxx with your actual authorization header.
Check that the response was successful, and the result has a value; if not, print the error and call the completion handler.
Using SwiftyJSON, retrieve the image_colors array from the response. Iterate over each dictionary object in the image_colors array, and transform it into a PhotoColor object. This object pairs colors in the RGB format with the color name as a string.
Call the completion handler, passing in the photoColors from the service.
Finally, go back to upload(image:progress:completion:) and replace the call to downloadTags(contentID:) in the success condition with the following:
self.downloadTags(contentID: firstFileID) { tags in self.downloadColors(contentID: firstFileID) { colors in completion(tags, colors) } }
This nests the operations of uploading the image, downloading tags and downloading colors.
Build and run your project again; this time, you should see the returned color tags when you select the Colors button:
This uses the RGB colors you mapped to PhotoColor structs to change the background color of the view. You've now successfully uploaded an image to Imagga and fetched data from two different endpoints. You've come a long way, but there's some room for improvement in how you're using Alamofire in PhotoTagger.
Improving PhotoTagger
You probably noticed some repeated code in PhotoTagger. If Imagga released v2 of their API and deprecated v1, PhotoTagger would no longer function and you'd have to update the URL in each of the three methods. Similarly, if your authorization token changed you'd be updating it all over the place.
Alamofire provides a simple method to eliminate this code duplication and provide centralized configuration. The technique involves creating a struct conforming to URLRequestConvertible and updating your upload and request calls.
Create a new Swift file by clicking File\New\File... and selecting Swift file under iOS. Click Next, name the file ImaggaRouter.swift, select the Group PhotoTagger with the yellow folder icon and click Create.
Add the following to your new file:
import Alamofire public enum ImaggaRouter: URLRequestConvertible { // 1 enum Constants { static let baseURLPath = "http://api.imagga.com/v1" static let authenticationToken = "Basic xxx" } // 2 case content case tags(String) case colors(String) // 3 var method: HTTPMethod { switch self { case .content: return .post case .tags, .colors: return .get } } // 4 var path: String { switch self { case .content: return "/content" case .tags: return "/tagging" case .colors: return "/colors" } } // 5 var parameters: [String: Any] { switch self { case .tags(let contentID): return ["content": contentID] case .colors(let contentID): return ["content": contentID, "extract_object_colors": 0] default: return [:] } } // 6 public func asURLRequest() throws -> URLRequest { let url = try Constants.baseURLPath.asURL() var request = URLRequest(url: url.appendingPathComponent(path)) request.httpMethod = method.rawValue request.setValue(Constants.authenticationToken, forHTTPHeaderField: "Authorization") request.timeoutInterval = TimeInterval(10 * 1000) return try URLEncoding.default.encode(request, with: parameters) } }
Here's a step-by-step explanation of the above code:
Declare constants to hold the Imagga base URL and your Basic xxx with your actual authorization header.
Declare the enum cases. Each case corresponds to an api endpoint.
Return the HTTP method for each api endpoint.
Return the path for each api endpoint.
Return the parameters for each api endpoint.
Use all of the above components to create a URLRequest for the requested endpoint.
Now all your boilerplate code is in single place, should you ever need to update it.
Go back to ViewController.swift and in upload(image:progress:completion:) replace:
Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(imageData, withName: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, to: "http://api.imagga.com/v1/content", headers: ["Authorization": "Basic xxx"],
with the following:
Alamofire.upload(multipartFormData: { multipartFormData in multipartFormData.append(imageData, withName: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, with: ImaggaRouter.content,
Next replace the call for Alamofire.request in downloadTags(contentID:completion:) with:
Alamofire.request(ImaggaRouter.tags(contentID))
Finally, update the call to Alamofire.request in downloadColors(contentID:completion:) with:
Alamofire.request(ImaggaRouter.colors(contentID))
Note: Be sure to leave the responseJSON handlers in place for both of the previous edits.
Build and run for the final time; everything should function just as before, which means you've refactored everything without breaking your app. However, you don't have to go through your entire source code if anything on the Imagga integration ever changes: APIs, your authorization token, parameters, etc. Awesome job!
Where To Go From Here?
You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial. Don't forget to replace your authorization token as appropriate!
This tutorial covered the very basics. You can take a deeper dive by looking at the documentation on the Alamofire site at https://github.com/Alamofire/Alamofire.
Also, you can take some time to learn more about Apple's URLSession which Alamofire uses under the hood:
Apple WWDC 2015 - 711 - Networking with NSURLSession
Apple URL Session Programming Guide
Ray Wenderlich - NSURLSession Tutorial
Please share any comments or questions about this tutorial in the forum discussion below!
The post Alamofire Tutorial: Getting Started appeared first on Ray Wenderlich.
Alamofire Tutorial: Getting Started published first on https://medium.com/@koresol
0 notes
Link
“Most of your data is available to you when you log into your Facebook account. For example, you can look at your Timeline to see posts you've shared and photos you've been tagged in, while Messages contains your chat history. You can also go to your Activity Log to see a history of actions you have taken on Facebook. This includes posts you have liked or commented on, apps you have used and anything you've searched for.”
[Option 2:] “I don't have a Facebook account” [emphasis added]
“Please provide us with the following information [there is a form, not shown] and we'll follow up. Alternatively, you can email: [email protected]”
-----------
https://slate.com/technology/2018/04/facebook-collects-data-on-non-facebook-users-if-they-want-to-delete-it-they-have-to-sign-up.html
So you *can* check and see if FB has a “shadow profile” on you by emailing to the above. But it seems you can’t prevent them from creating or maintaining the shadow profile if you aren’t a FB user.
0 notes
Text
Alamofire Tutorial: Getting Started
Update note: This tutorial has been updated to Xcode 9.3, iOS 11.3, Swift 4.1 and Alamofire 4.7.0 by Ron Kliffer. The original tutorial was written by Aaron Douglas.
Get the lowdown on Alamofire!
Alamofire is a Swift-based HTTP networking library for iOS and Mac OS X. It provides an elegant interface on top of Apple’s Foundation networking stack that simplifies a number of common networking tasks.
Alamofire provides chainable request/response methods, JSON parameter and response serialization, authentication, and many other features.
In this Alamofire tutorial, you’ll use Alamofire to perform basic networking tasks like uploading files and requesting data from a third-party RESTful API.
Alamofire’s elegance comes from the fact it was written from the ground up in Swift and does not inherit anything from its Objective-C counterpart, AFNetworking.
You should have a conceptual understanding of HTTP networking and some exposure to Apple’s networking classes such as URLSession.
While Alamofire does obscure some implementation details, it’s good to have some background knowledge if you ever need to troubleshoot your network requests.
Getting Started
Use the Download Materials button at the top or bottom of this tutorial to download the starter project.
Note: Alamofire is normally integrated using CocoaPods. It has already been installed for you in the downloaded projects.
The app for this Alamofire tutorial is named PhotoTagger. When complete, it will let you select an image from your library (or camera if you’re running on an actual device) and upload the image to a third-party service called Imagga. This service will perform some image recognition tasks to come up with a list of tags and primary colors for the image:
This project uses CocoaPods, so open it using the PhotoTagger.xcworkspace file.
Note:To learn more about CocoaPods, check out this tutorial by Joshua Greene, published right here on the site.
Build and run the project. You’ll see the following:
Click Select Photo and choose a photo. The background image will be replaced with the image you chose.
Open Main.storyboard and you’ll see the additional screens for displaying tags and colors have been added for you. All that remains is to upload the image and fetch the tags and colors.
The Imagga API
Imagga is an image recognition Platform-as-a-Service that provides image tagging APIs for developers and businesses to build scalable, image-intensive cloud apps. You can play around with a demo of their auto-tagging service here.
You’ll need to create a free developer account with Imagga for this Alamofire tutorial. Imagga requires an authorization header in each HTTP request so only people with an account can use their services. Go to https://imagga.com/auth/signup/hacker and fill out the form. After you create your account, check out the dashboard:
Listed down in the Authorization section is a secret token you’ll use later. You’ll need to include this information with every HTTP request as a header.
Note: Make sure you copy the whole secret token, be sure to scroll over to the right and verify you copied everything.
You’ll be using Imagga’s content endpoint to upload the photos, tagging endpoint for the image recognition and colors endpoint for color identification. You can read all about the Imagga API at http://docs.imagga.com.
REST, HTTP, JSON — What’s that?
If you’re coming to this tutorial with very little experience in using third-party services over the Internet, you might be wondering what all those acronyms mean! :]
HTTP is the application protocol, or set of rules, web sites use to transfer data from the web server to your screen. You’ve seen HTTP (or HTTPS) listed in the front of every URL you type into a web browser. You might have heard of other application protocols, such as FTP, Telnet, and SSH. HTTP defines several request methods, or verbs, the client (your web browser or app) use to indicate the desired action:
GET: Retrieves data, such as a web page, but doesn’t alter any data on the server.
HEAD: Identical to GET but only sends back the headers and none of the actual data.
POST: Sends data to the server, commonly used when filling a form and clicking submit.
PUT: Sends data to the specific location provided.
DELETE: Deletes data from the specific location provided.
REST, or REpresentational State Transfer, is a set of rules for designing consistent, easy-to-use and maintainable web APIs. REST has several architecture rules that enforce things such as not persisting states across requests, making requests cacheable, and providing uniform interfaces. This makes it easy for app developers like you to integrate the API into your app, without needing to track the state of data across requests.
JSON stands for JavaScript Object Notation. It provides a straightforward, human-readable and portable mechanism for transporting data between two systems. JSON has a limited number of data types: string, boolean, array, object/dictionary, null and number. There’s no distinction between integers and decimals.
There are a few native choices for converting your objects in memory to JSON and vice-versa: the good old JSONSerialization class and the newly-added JSONEncoder and JSONDecoder classes. In addition, there are numerous third party libraries that help with handling JSON. You’ll use one of them, SwiftyJSON in this tutorial.
The combination of HTTP, REST and JSON make up a good portion of the web services available to you as a developer. Trying to understand how every little piece works can be overwhelming. Libraries like Alamofire can help reduce the complexity of working with these services, and get you up and running faster than you could without their help.
What is Alamofire Good For?
Why do you need Alamofire at all? Apple already provides URLSession and other classes for downloading content via HTTP, so why complicate things with another third party library?
The short answer is Alamofire is based on URLSession, but it frees you from writing boilerplate code which makes writing networking code much easier. You can access data on the Internet with very little effort, and your code will be much cleaner and easier to read.
There are several major functions available with Alamofire:
Alamofire.upload: Upload files with multipart, stream, file or data methods.
Alamofire.download: Download files or resume a download already in progress.
Alamofire.request: Every other HTTP request not associated with file transfers.
These Alamofire methods are global within Alamofire so you don’t have to instantiate a class to use them. There are underlying pieces to Alamofire that are classes and structs, like SessionManager, DataRequest, and DataResponse; however, you don’t need to fully understand the entire structure of Alamofire to start using it.
Here’s an example of the same networking operation with both Apple’s URLSession and Alamofire’s request function:
// With URLSession public func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) { guard let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true") else { completion(nil) return } var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0 * 1000) urlRequest.httpMethod = "GET" urlRequest.addValue("application/json", forHTTPHeaderField: "Accept") let task = urlSession.dataTask(with: urlRequest) { (data, response, error) -> Void in guard error == nil else { print("Error while fetching remote rooms: \(String(describing: error)") completion(nil) return } guard let data = data, let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { print("Nil data received from fetchAllRooms service") completion(nil) return } guard let rows = json?["rows"] as? [[String: Any]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } let rooms = rows.flatMap { roomDict in return RemoteRoom(jsonData: roomDict) } completion(rooms) } task.resume() }
Versus:
// With Alamofire func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) { guard let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true") else { completion(nil) return } Alamofire.request(url, method: .get, parameters: ["include_docs": "true"]) .validate() .responseJSON { response in guard response.result.isSuccess else { print("Error while fetching remote rooms: \(String(describing: response.result.error)") completion(nil) return } guard let value = response.result.value as? [String: Any], let rows = value["rows"] as? [[String: Any]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } let rooms = rows.flatMap { roomDict in return RemoteRoom(jsonData: roomDict) } completion(rooms) } }
You can see the required setup for Alamofire is shorter and it’s much clearer what the function does. You deserialize the response with responseJSON(options:completionHandler:) and calling validate() to verify the response status code is in the default acceptable range between 200 and 299 simplifies error condition handling.
Now the theory is out of the way, it’s time to start using Alamofire.
Uploading Files
Open ViewController.swift and add the following to the top, below import SwiftyJSON:
import Alamofire
This lets you use the functionality provided by the Alamofire module in your code, which you’ll be doing soon!
Next, go to imagePickerController(_:didFinishPickingMediaWithInfo:) and add the following to the end, right before the call to dismiss(animated:):
// 1 takePictureButton.isHidden = true progressView.progress = 0.0 progressView.isHidden = false activityIndicatorView.startAnimating() upload(image: image, progressCompletion: { [weak self] percent in // 2 self?.progressView.setProgress(percent, animated: true) }, completion: { [weak self] tags, colors in // 3 self?.takePictureButton.isHidden = false self?.progressView.isHidden = true self?.activityIndicatorView.stopAnimating() self?.tags = tags self?.colors = colors // 4 self?.performSegue(withIdentifier: "ShowResults", sender: self) })
Everything with Alamofire is asynchronous, which means you’ll update the UI in an asynchronous manner:
Hide the upload button, and show the progress view and activity view.
While the file uploads, you call the progress handler with an updated percent. This updates the progress indicator of the progress bar.
The completion handler executes when the upload finishes. This sets the controls back to their original state.
Finally the Storyboard advances to the results screen when the upload completes, successfully or not. The user interface doesn’t change based on the error condition.
Next, find upload(image:progressCompletion:completion:) at the bottom of the file. It is currently only a method stub, so give it the following implementation:
func upload(image: UIImage, progressCompletion: @escaping (_ percent: Float) -> Void, completion: @escaping (_ tags: [String]?, _ colors: [PhotoColor]?) -> Void) { // 1 guard let imageData = UIImageJPEGRepresentation(image, 0.5) else { print("Could not get JPEG representation of UIImage") return } // 2 Alamofire.upload(multipartFormData: { multipartFormData in multipartFormData.append(imageData, withName: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, to: "http://api.imagga.com/v1/content", headers: ["Authorization": "Basic xxx"], encodingCompletion: { encodingResult in }) }
Here’s what’s happening:
The image that’s being uploaded needs to be converted to a Data instance.
Here you convert the JPEG data blob (imageData) into a MIME multipart request to send to the Imagga content endpoint.
Note: Make sure to replace Basic xxx with the actual authorization header taken from the Imagga dashboard.
Next, add the following to the encodingCompletion closure:
switch encodingResult { case .success(let upload, _, _): upload.uploadProgress { progress in progressCompletion(Float(progress.fractionCompleted)) } upload.validate() upload.responseJSON { response in } case .failure(let encodingError): print(encodingError) }
This chunk of code calls the Alamofire upload function and passes in a small calculation to update the progress bar as the file uploads. It then validates the response has a status code in the default acceptable range between 200 and 299.
Note: Prior to Alamofire 4 it was not guaranteed progress callbacks were called on the main queue. Beginning with Alamofire 4, the new progress callback API is always called on the main queue.
Next, add the following code to the upload.responseJSON closure:
// 1 guard response.result.isSuccess, let value = response.result.value else { print("Error while uploading file: \(String(describing: response.result.error))") completion(nil, nil) return } // 2 let firstFileID = JSON(value)["uploaded"][0]["id"].stringValue print("Content uploaded with ID: \(firstFileID)") //3 completion(nil, nil)
Here’s a step-by-step explanation of the above code:
Check that the upload was successful, and the result has a value; if not, print the error and call the completion handler.
Using SwiftyJSON, retrieve the firstFileID from the response.
Call the completion handler to update the UI. At this point, you don’t have any downloaded tags or colors, so simply call this with no data.
Note: Every response has a Result enum with a value and type. Using automatic validation, the result is considered a success when it returns a valid HTTP Code between 200 and 299 and the Content Type is of a valid type specified in the Accept HTTP header field.
You can perform manual validation by adding .validate options as shown below:
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]) .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .response { response in // response handling code }
The UI won't show an error if you hit an error during the upload; it merely returns no tags or colors to the user. This isn't the best user experience, but it's fine for this tutorial.
Build and run your project; select an image and watch the progress bar change as the file uploads. You should see a note like the following in your console when the upload completes:
Congratulations, you've successfully uploaded a file over the Interwebs!
Retrieving Data
The next step after uploading the image to Imagga is to fetch the tags Imagga produces after it analyzes the photo.
Add the following method to the ViewController extension below upload(image:progress:completion:):
func downloadTags(contentID: String, completion: @escaping ([String]?) -> Void) { // 1 Alamofire.request("http://api.imagga.com/v1/tagging", parameters: ["content": contentID], headers: ["Authorization": "Basic xxx"]) // 2 .responseJSON { response in guard response.result.isSuccess, let value = response.result.value else { print("Error while fetching tags: \(String(describing: response.result.error))") completion(nil) return } // 3 let tags = JSON(value)["results"][0]["tags"].array?.map { json in json["tag"].stringValue } // 4 completion(tags) } }
Here's a step-by-step explanation of the above code:
Perform an HTTP GET request against the tagging endpoint, sending the URL parameter content with the ID you received after the upload. Again, be sure to replace Basic xxx with your actual authorization header.
Check that the response was successful, and the result has a value; if not, print the error and call the completion handler.
Using SwiftyJSON, retrieve the raw tags array from the response. Iterate over each dictionary object in the tags array, retrieving the value associated with the tag key.
Call the completion handler passing in the tags received from the service.
Next, go back to upload(image:progress:completion:) and replace the call to the completion handler in the success condition with the following:
self.downloadTags(contentID: firstFileID) { tags in completion(tags, nil) }
This simply sends along the tags to the completion handler.
Build and run your project; select a photo and you should see something similar to the following appear:
Pretty slick! That Imagga is one smart API. :] Next, you'll fetch the colors of the image.
Add the following method to the ViewController extension below downloadTags(contentID:completion:):
func downloadColors(contentID: String, completion: @escaping ([PhotoColor]?) -> Void) { // 1. Alamofire.request("http://api.imagga.com/v1/colors", parameters: ["content": contentID], headers: ["Authorization": "Basic xxx"]) .responseJSON { response in // 2 guard response.result.isSuccess, let value = response.result.value else { print("Error while fetching colors: \(String(describing: response.result.error))") completion(nil) return } // 3 let photoColors = JSON(value)["results"][0]["info"]["image_colors"].array?.map { json in PhotoColor(red: json["r"].intValue, green: json["g"].intValue, blue: json["b"].intValue, colorName: json["closest_palette_color"].stringValue) } // 4 completion(photoColors) } }
Taking each numbered comment in turn:
Perform an HTTP GET request against the colors endpoint, sending the URL parameter content with the ID you received after the upload. Again, be sure to replace Basic xxx with your actual authorization header.
Check that the response was successful, and the result has a value; if not, print the error and call the completion handler.
Using SwiftyJSON, retrieve the image_colors array from the response. Iterate over each dictionary object in the image_colors array, and transform it into a PhotoColor object. This object pairs colors in the RGB format with the color name as a string.
Call the completion handler, passing in the photoColors from the service.
Finally, go back to upload(image:progress:completion:) and replace the call to downloadTags(contentID:) in the success condition with the following:
self.downloadTags(contentID: firstFileID) { tags in self.downloadColors(contentID: firstFileID) { colors in completion(tags, colors) } }
This nests the operations of uploading the image, downloading tags and downloading colors.
Build and run your project again; this time, you should see the returned color tags when you select the Colors button:
This uses the RGB colors you mapped to PhotoColor structs to change the background color of the view. You've now successfully uploaded an image to Imagga and fetched data from two different endpoints. You've come a long way, but there's some room for improvement in how you're using Alamofire in PhotoTagger.
Improving PhotoTagger
You probably noticed some repeated code in PhotoTagger. If Imagga released v2 of their API and deprecated v1, PhotoTagger would no longer function and you'd have to update the URL in each of the three methods. Similarly, if your authorization token changed you'd be updating it all over the place.
Alamofire provides a simple method to eliminate this code duplication and provide centralized configuration. The technique involves creating a struct conforming to URLRequestConvertible and updating your upload and request calls.
Create a new Swift file by clicking File\New\File... and selecting Swift file under iOS. Click Next, name the file ImaggaRouter.swift, select the Group PhotoTagger with the yellow folder icon and click Create.
Add the following to your new file:
import Alamofire public enum ImaggaRouter: URLRequestConvertible { // 1 enum Constants { static let baseURLPath = "http://api.imagga.com/v1" static let authenticationToken = "Basic xxx" } // 2 case content case tags(String) case colors(String) // 3 var method: HTTPMethod { switch self { case .content: return .post case .tags, .colors: return .get } } // 4 var path: String { switch self { case .content: return "/content" case .tags: return "/tagging" case .colors: return "/colors" } } // 5 var parameters: [String: Any] { switch self { case .tags(let contentID): return ["content": contentID] case .colors(let contentID): return ["content": contentID, "extract_object_colors": 0] default: return [:] } } // 6 public func asURLRequest() throws -> URLRequest { let url = try Constants.baseURLPath.asURL() var request = URLRequest(url: url.appendingPathComponent(path)) request.httpMethod = method.rawValue request.setValue(Constants.authenticationToken, forHTTPHeaderField: "Authorization") request.timeoutInterval = TimeInterval(10 * 1000) return try URLEncoding.default.encode(request, with: parameters) } }
Here's a step-by-step explanation of the above code:
Declare constants to hold the Imagga base URL and your Basic xxx with your actual authorization header.
Declare the enum cases. Each case corresponds to an api endpoint.
Return the HTTP method for each api endpoint.
Return the path for each api endpoint.
Return the parameters for each api endpoint.
Use all of the above components to create a URLRequest for the requested endpoint.
Now all your boilerplate code is in single place, should you ever need to update it.
Go back to ViewController.swift and in upload(image:progress:completion:) replace:
Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(imageData, withName: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, to: "http://api.imagga.com/v1/content", headers: ["Authorization": "Basic xxx"],
with the following:
Alamofire.upload(multipartFormData: { multipartFormData in multipartFormData.append(imageData, withName: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, with: ImaggaRouter.content,
Next replace the call for Alamofire.request in downloadTags(contentID:completion:) with:
Alamofire.request(ImaggaRouter.tags(contentID))
Finally, update the call to Alamofire.request in downloadColors(contentID:completion:) with:
Alamofire.request(ImaggaRouter.colors(contentID))
Note: Be sure to leave the responseJSON handlers in place for both of the previous edits.
Build and run for the final time; everything should function just as before, which means you've refactored everything without breaking your app. However, you don't have to go through your entire source code if anything on the Imagga integration ever changes: APIs, your authorization token, parameters, etc. Awesome job!
Where To Go From Here?
You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial. Don't forget to replace your authorization token as appropriate!
This tutorial covered the very basics. You can take a deeper dive by looking at the documentation on the Alamofire site at https://github.com/Alamofire/Alamofire.
Also, you can take some time to learn more about Apple's URLSession which Alamofire uses under the hood:
Apple WWDC 2015 - 711 - Networking with NSURLSession
Apple URL Session Programming Guide
Ray Wenderlich - NSURLSession Tutorial
Please share any comments or questions about this tutorial in the forum discussion below!
The post Alamofire Tutorial: Getting Started appeared first on Ray Wenderlich.
Alamofire Tutorial: Getting Started published first on https://medium.com/@koresol
0 notes