3

It seems for me this is a very simple task, but even after a lot of researching and trying I can't get it working...

So I have for example this URL, for what I understand this is a api to an JSONObject?!

http://api.geekdo.com/api/images?ajax=1&gallery=all&nosession=1&objectid=127023&objecttype=thing&pageid=357&showcount=1&size=thumb&sort=recent

If I open this link in my browser I get the following result:

{"images":[{"imageid":"1567153","imageurl_lg":"https://cf.geekdo-images.com/images/pic1567153_lg.jpg","name":null,"caption":"White power tiles","numrecommend":"6","numcomments":"0","user":{"username":"manosdowns","avatar":"1","avatarfile":"avatar_id33829.jpg"},"imageurl":"https://cf.geekdo-images.com/6fCr14v025ZKYhXRMnbhYR16Ta8=/fit-in/200x150/pic1567153.jpg"}],"config":{"sorttypes":[{"type":"hot","name":"Hot"},{"type":"recent","name":"Recent"}],"numitems":402,"endpage":402,"galleries":[{"type":"all","name":"All"},{"type":"game","name":"Game"},{"type":"people","name":"People"},{"type":"creative","name":"Creative"}],"categories":[{"type":"","name":"All"},{"type":"BoxFront","name":"BoxFront"},{"type":"BoxBack","name":"BoxBack"},{"type":"Components","name":"Components"},{"type":"Customized","name":"Customized"},{"type":"Play","name":"Play"},{"type":"Miscellaneous","name":"Miscellaneous"},{"type":"Mature","name":"Mature"},{"type":"uncat","name":"Uncategorized"}],"licensefilters":[{"type":"","name":"Any"},{"type":"reuse","name":"Copying allowed"},{"type":"commercial","name":"Commercial use allowed"},{"type":"modify","name":"Modification allowed"}],"datefilters":[{"value":"alltime","name":"All Time"},{"value":"today","name":"Today"},{"value":"twodays","name":"Two Days"},{"value":"last7","name":"Last 7 Days"},{"value":"last30","name":"Last 30 Days"},{"value":"year","name":"Last 365 Days"}],"filters":[{"name":"Licenses","listname":"licensefilters","type":"licensefilter"},{"name":"Category","listname":"categories","type":"tag"},{"name":"Gallery","listname":"galleries","type":"gallery"}]}}

Now my first attempt was to parse this link the way I parse homepages:

    guard let myURL = URL(string: link) else {                                                 >             print("Error: \(link) doesn't seem to be a valid URL")
        return
    }

    do {
        link = try String(contentsOf: myURL, encoding: .ascii)
    } catch let error {
        print("Error: \(error)")
    }

But this doesn't work as I now understand that's because this is JSON coded?!

I searched for parsing JSON and found some explanations for encoding and decoding, but my problem is that in all examples given, the explanations start by "having" already the contents of the JsonObject. My problem is that I can read the contents of the URL in the browser but I would need the content of the URL in Xcode itself, so I can parse it?!

So in my specific case I would only need the content of "imageurl_lg"

...I would know how to do it if I could display the content I can see in my browser also in Xcode - but how do I get the contents of the link into Xcode?

For reference, I also read following instructions, but couldn't apply them to my example... https://www.raywenderlich.com/172145/encoding-decoding-and-serialization-in-swift-4

https://grokswift.com/json-swift-4/

and some more but they didn't help me...

2 Answers 2

8

You need to use a URLSession task to do this, and after that you need to use JSONSerialization in this example I return a dictionary of [String:Any] you can convert it to whatever Model you need

Use this Code

func fetchData(completion: @escaping ([String:Any]?, Error?) -> Void) {
    let url = URL(string: "http://api.geekdo.com/api/images?ajax=1&gallery=all&nosession=1&objectid=127023&objecttype=thing&pageid=357&showcount=1&size=thumb&sort=recent")!

    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else { return }
        do {
            if let array = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any]{
                completion(array, nil)
            }
        } catch {
            print(error)
            completion(nil, error)
        }
    }
    task.resume()
}

How use it

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    fetchData { (dict, error) in
        debugPrint(dict)
    }
}

Result Log printed

Optional(["config": {
    categories =     (
                {
            name = All;
            type = "";
        },
                {
            name = BoxFront;
            type = BoxFront;
        },
                {
            name = BoxBack;
            type = BoxBack;
        },
                {
            name = Components;
            type = Components;
        },
                {
            name = Customized;
            type = Customized;
        },
                {
            name = Play;
            type = Play;
        },
                {
            name = Miscellaneous;
            type = Miscellaneous;
        },
                {
            name = Mature;
            type = Mature;
        },
                {
            name = Uncategorized;
            type = uncat;
        }
    );
    datefilters =     (
                {
            name = "All Time";
            value = alltime;
        },
                {
            name = Today;
            value = today;
        },
                {
            name = "Two Days";
            value = twodays;
        },
                {
            name = "Last 7 Days";
            value = last7;
        },
                {
            name = "Last 30 Days";
            value = last30;
        },
                {
            name = "Last 365 Days";
            value = year;
        }
    );
    endpage = 402;
    filters =     (
                {
            listname = licensefilters;
            name = Licenses;
            type = licensefilter;
        },
                {
            listname = categories;
            name = Category;
            type = tag;
        },
                {
            listname = galleries;
            name = Gallery;
            type = gallery;
        }
    );
    galleries =     (
                {
            name = All;
            type = all;
        },
                {
            name = Game;
            type = game;
        },
                {
            name = People;
            type = people;
        },
                {
            name = Creative;
            type = creative;
        }
    );
    licensefilters =     (
                {
            name = Any;
            type = "";
        },
                {
            name = "Copying allowed";
            type = reuse;
        },
                {
            name = "Commercial use allowed";
            type = commercial;
        },
                {
            name = "Modification allowed";
            type = modify;
        }
    );
    numitems = 402;
    sorttypes =     (
                {
            name = Hot;
            type = hot;
        },
                {
            name = Recent;
            type = recent;
        }
    ); }, "images": <__NSSingleObjectArrayI 0x600000010710>( {
    caption = "White power tiles";
    imageid = 1567153;
    imageurl = "https://cf.geekdo-images.com/6fCr14v025ZKYhXRMnbhYR16Ta8=/fit-in/200x150/pic1567153.jpg";
    "imageurl_lg" = "https://cf.geekdo-images.com/images/pic1567153_lg.jpg";
    name = "<null>";
    numcomments = 0;
    numrecommend = 6;
    user =     {
        avatar = 1;
        avatarfile = "avatar_id33829.jpg";
        username = manosdowns;
    }; } ) ])

Updated fixing "App Transport Security has blocked a cleartext" error

Adjust your info.plist

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, but now I get this error: App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file. Cannot start load of Task <9D4A545D-E380-4B17-A090-9C344F6688AC>.<1> since it does not conform to ATS policy Task <9D4A545D-E380-4B17-A090-9C344F6688AC>.<1> finished with error - code: -1022
@MakiCodes add those keys in your info.plist check my updated answer
@MakiCodes let me know if you need further assistance
Nevermind, it was enough to change the link to "https". A BIG "Thank you"!
1

So this is what playground gave me.

import UIKit

var str = "Hello, playground"

func makeGetCall() {
    guard let myURL = URL(string: "http://api.geekdo.com/api/images?ajax=1&gallery=all&nosession=1&objectid=127023&objecttype=thing&pageid=357&showcount=1&size=thumb&sort=recent") else {
        print("Error: \(link) doesn't seem to be a valid URL")
        return
    }

    do {
        var content = try String(contentsOf: myURL, encoding: .ascii)
        print("Content: \(content)")
    } catch let error {
        print("Error: \(error)")
    }
}
makeGetCall()

Prints

enter image description here

4 Comments

Please don't suggest synchronous methods like String(contentsOf. The method is inappropriate.
This not a suggestion, simple correction of OP's own answer. I see a better answer above so no need for me to post more.
@Gihan Thank you too! I know synchronous methods are not the preferred way, but you answered my question the way I asked! (And I might need to implement your solution, too!)
No problem, while you are learning keep up with the good practice. And you may want to upvote @einier s answer as It is well detailed and will help you. Cheers

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.