How can I run a if else function based on results of bool value - ios

Using the results of JSON to either run a function or display an alert.
First a function called CheckStruct() should run.
The results of this function is either true or false using the following structure:
struct CheckStruct: Codable {
let error: Bool?
}
If the function result is Optional(false) one thing should happen.
If the function result is Optional(true) an alert should appear.
The following is JSONFunc()
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
print(myData.error)
} catch {
print(error)
}
}.resume()
}

You can try
let myData = try JSONDecoder().decode(CheckStruct.self, from:data)
if myData.error == true {
// true
}
else {
// false or nil
}

try this
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData = try JSONDecoder().decode(CheckStruct.self, from:data)
guard let error = myData.error else {return}
if error {
//dotour stuff
}else{
//do other stuff
}
//or this code using ternary operator
// error == true ? /*do stuff here*/: //do another stuff if false
} catch {
print(error)
}
}.resume()
}enter code here

Related

How to print the api response to the console? Xcode 14

I am learning about API's and decided to practice using them by writing a simple function to call an api and print the response. The issues I am having is that the response is not printing to the console. I am also new to Swift but watched a couple of tutorials, which lead me to write this basic skeleton code.
import Foundation
struct Posts: Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
func fetch() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
do {
let posts = try JSONDecoder().decode(Posts.self, from: data)
print(posts) //Doesn't print the response
}
catch {
print(error)
}
}
task.resume()
}
fetch()
func fetch() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
do {
let posts = try JSONDecoder().decode([Posts].self, from: data)
print(posts)
}
catch {
print(error)
}
}
task.resume()
}

SKCloudServiceController().requestUserToken Freezes on iOS 14.2

I am trying to run the following function from SKCloudServiceController but for some reason every time it runs, the app just freezes. I have tested my developer token and it does work. I am running Xcode 12.2. Maybe there was an update which would make this not work anymore?
I've tested the token and it works.
class AppleMusicAPI {
let developerToken = "b'eyJ0{...}RDlRSlFw'"
func getUserToken() -> String {
var userToken = String()
let lock = DispatchSemaphore(value: 0)
func requestAccess(_ completion: #escaping(String?) -> Void) {
SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken) { (receivedToken, error) in
completion(receivedToken)
}
}
requestAccess( { (completeToken) in
if let token = completeToken {
userToken = token
lock.signal()
}
})
lock.wait()
return userToken
}
func fetchStorefrontID() -> String {
let lock = DispatchSemaphore(value: 0)
var storefrontID: String!
let musicURL = URL(string: "https://api.music.apple.com/v1/me/storefront")!
var musicRequest = URLRequest(url: musicURL)
musicRequest.httpMethod = "GET"
musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
musicRequest.addValue(getUserToken(), forHTTPHeaderField: "Music-User-Token")
URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
guard error == nil else { return }
if let json = try? JSON(data: data!) {
let result = (json["data"]).array!
let id = (result[0].dictionaryValue)["id"]!
storefrontID = id.stringValue
lock.signal()
}
}.resume()
lock.wait()
return storefrontID
}
func searchAppleMusic(_ searchTerm: String!) -> [Song] {
let lock = DispatchSemaphore(value: 0)
var songs = [Song]()
let musicURL = URL(string: "https://api.music.apple.com/v1/catalog/\(fetchStorefrontID())/search?term=\(searchTerm.replacingOccurrences(of: " ", with: "+"))&types=songs&limit=25")!
var musicRequest = URLRequest(url: musicURL)
musicRequest.httpMethod = "GET"
musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
musicRequest.addValue(getUserToken(), forHTTPHeaderField: "Music-User-Token")
URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
guard error == nil else { return }
if let json = try? JSON(data: data!) {
let result = (json["results"]["songs"]["data"]).array!
for song in result {
let attributes = song["attributes"]
let currentSong = Song(id: attributes["playParams"]["id"].string!, name: attributes["name"].string!, artistName: attributes["artistName"].string!, artworkURL: attributes["artwork"]["url"].string!)
songs.append(currentSong)
}
lock.signal()
} else {
lock.signal()
}
}.resume()
lock.wait()
return songs
}
}
I have a theory on what happened: since the requestUserToken function is called on the main thread, using a semaphore creates an infinite wait(lock.wait() and lock.signal() are called on the same thread). What eventually worked for me was using completion handlers instead of semaphores. So my getUserToken function looked like this:
func getUserToken(completion: #escaping(_ userToken: String) -> Void) -> String {
SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken) { (userToken, error) in
guard error == nil else {
return
}
completion(userToken)
}
}
And in any subsequent functions that need the userToken, I passed it in as a parameter:
func fetchStorefrontID(userToken: String, completion: #escaping(String) -> Void){
var storefrontID: String!
let musicURL = URL(string: "https://api.music.apple.com/v1/me/storefront")!
var musicRequest = URLRequest(url: musicURL)
musicRequest.httpMethod = "GET"
musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
musicRequest.addValue(userToken, forHTTPHeaderField: "Music-User-Token")
URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
guard error == nil else { return }
if let json = try? JSON(data: data!) {
let result = (json["data"]).array!
let id = (result[0].dictionaryValue)["id"]!
storefrontID = id.stringValue
completion(storefrontID)
}
}.resume()
}
Calling fetchStorefrontID by first calling getUserToken then calling fetchStorefrontID in its completion handler
getUserToken{ userToken in
fetchStorefrontID(userToken){ storefrontID in
print(storefrontID)
//anything you want to do with storefrontID here
}
}
This is just what eventually worked for me.
Cleaning up a little of what has already been posted.
func getUserToken(completion: #escaping(_ userToken: String?) -> Void) {
SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken) { (receivedToken, error) in
guard error == nil else { return }
completion(receivedToken)
}
}
func fetchStorefrontID(userToken: String, completion: #escaping(String) -> Void) {
var storefrontID: String! = ""
let musicURL = URL(string: "https://api.music.apple.com/v1/me/storefront")!
var musicRequest = URLRequest(url: musicURL)
musicRequest.httpMethod = "GET"
musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
musicRequest.addValue(userToken, forHTTPHeaderField: "Music-User-Token")
URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
guard error == nil else { return }
if let json = try? JSON(data: data!) {
let result = (json["data"]).array!
let id = (result[0].dictionaryValue)["id"]!
storefrontID = id.stringValue
completion(storefrontID)
}
}.resume()
}
And then to call that code:
SKCloudServiceController.requestAuthorization { status in
if status == .authorized {
let api = AppleMusicAPI()
api.getUserToken { userToken in
guard let userToken = userToken else {
return
}
api.fetchStorefrontID(userToken: userToken) { data in
print(data)
}
}
}
}

Keep getting the error “Expected to decode Array<Any> but found a dictionary | Swift

I have the following JSON that is formatted like this:
{
"error":false
}
I understand that is not an array because it does not include square brackets on both sides, but I cannot seem to understand how to properly get Swift to interpret this correctly.
This is the structure I am using:
struct CheckStruct: Decodable {
let error: String?
}
And the following is the function that should read the JSON:
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.CheckRecord = try JSONDecoder().decode(Array<CheckStruct>.self,from:data)
DispatchQueue.main.async {
// Do something
}
}
catch {
print(error)
}
}.resume()
}
UPDATE:
If I were to use the results of the function to create an if else statement, how would this look?
For example if results are true do this..
else do this...
Your model should be like this:
struct CheckStruct: Codable {
let error: Bool?
}
And your function should be like this:
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
print(myData.error)
} catch {
print(error)
}
}.resume()
}
BONUS
//Create Typealias
typealias BoolHandler = ((Bool?) -> Void)
//Create Function with Completion
private func fetchData(_ completion: #escaping BoolHandler) {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
DispatchQueue.main.async {
completion(myData.error)
}
} catch {
DispatchQueue.main.async {
completion(nil)
}
}
}.resume()
}
//Call Method
fetchData { isSuccess in
if isSuccess {
// Do something
} else {
// Do something
}
}
I hope it will work for you.
Enjoy.

Store JSON data as a variable

I'm trying to figure out how to store JSON data into a variable for later use. How do I store it, and is it possible to use the variable in another view controller, or do I have to do another request to fetch the data?
This is my code:
#IBAction func signinTapped(_ sender: UIButton) {
guard let url = URL(string: "http://XXXXXX/TestReqIOS.php") else {
return
}
let email = txtEmail.text!
let password = txtPassword.text!
let data : Data = "loginSubmit=1&email=\(email)&password=\(password)&grant_type=password".data(using: .utf8)!
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type");
request.setValue(NSLocalizedString("lang", comment: ""), forHTTPHeaderField:"Accept-Language");
request.httpBody = data
print("Calling API")
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// vs let session = URLSession.shared
// make the request
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if let error = error {
print("error")
}
else if let response = response {
print("response")
}
else if let data = data {
print(data)
}
DispatchQueue.main.async { // Correct
guard let responseData = data else {
print("Error: did not receive data")
return
}
print(String(data: responseData, encoding: String.Encoding.utf8) ?? "")
}
})
task.resume()
}
Which will return:
{
"id": "7",
"first_name": "John",
"last_name": "Doe",
"email": "JohnDoe#text.com",
"created": "2019-03-11",
"modified": "2019-03-10",
}
It would be better to use a struct, such as in your case:
struct Data: Codable {
let id: Int
let first_name: String
let last_name: String
let email: String
let created: Date
let modified: Date
}
Then you create a variable of that struct where you will store it:
var dataVariable = [Data]()
Then you can do your URL call like:
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
let urlJSON = "URL"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let getData = try JSONDecoder().decode([Data].self, from: data)
self.dataVariable = getData
} catch let jsonErr {
print("error serializing json: \(jsonErr)")
}
completion(arr)
}.resume()
}
Then you can access all of this from the dataVariable var. IF you do this in a Manager class you can access it from any ViewController.
To access:
let firstNameString = dataVariable[0].first_name
If there are not multiple trees of the same, then just make sure its:
let getData = try JSONDecoder().decode(Data.self, from: data)
Edit:
In your case put the above here:
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if let error = error {
print("error")
}
else if let response = response {
print("response")
}
else if let data = data {
let getData = try JSONDecoder().decode([Data].self, from: data)
self.dataVariable = getData // <- Just decode here
print(data)
}
DispatchQueue.main.async { // Correct
guard let responseData = data else {
print("Error: did not receive data")
return
}
print(String(data: responseData, encoding: String.Encoding.utf8) ?? "")
}
})
task.resume()

Why will the object not append to the array?

class NetworkManager{
var articleList = [Article]()
func downloadJsonData() -> Void{
let jsonUrl = "someUrl"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { data, response, err in
//check err
//check response status
guard let data = data else { return }
do{
let apiResults = try JSONDecoder().decode(ApiResults.self, from: data)
//article list remains empty
self.articleList = apiResults.articles
} catch let err{
print(err)
}
}.resume()
}
}
I have also tried to use a for loop to append to the array and that didn't work either. Any help will be appreciated.
First thing I would check is that the data returned is correct.
Is the guard block triggering the return or is the data fine?
Is the JSON able to decode the response correctly?
Are the articles in the apiResults object populated.
The next thing is you are not attempting to append the contents of apiResults.articles to your list, instead you are making your list become what ever apiResults.articles is.
Try the following and see how it runs:
class NetworkManager{
// better declaration syntax
var articleList: [Article] = []
func downloadJsonData() {
let jsonUrl = "https://newsapi.org/v2/everything?sources=nfl-news&apiKey=mykey"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { data, response, err in
//check err
//check response status
guard let data = data else { return }
do{
let apiResults = try JSONDecoder().decode(ApiResults.self, from: data)
//article list remains empty
//appends contents instead of assignment
self.articleList.append(contentsOf: apiResults.articles)
} catch let err{
print(err)
}
}.resume()
}
}

Resources