📜 ⬆️ ⬇️

Swift + VK.API, or the story of SwiftyVK



Today I want to tell you about how I once met the Swift programming language and decided to write an application on it for the VKontakte social network under OSX (which, unfortunately, has not yet been completed). What pitfalls I had to face while curbing, at that time, a new language and crossing it with VK.API. To share with the public the result of what it all turned out and to try to justify why it was necessary to invent another bike in the form of a library to work with VK.API.
If someone is interested, then welcome under cat.

Part One: Hello, Swift!


It was the evening of the 2nd of June 2014. Being a user of Apple products and a novice objc developer, I was chained to the screen of my laptop, on which footage of the WWDC 2014 presentation flashed one after another. Spending one evening a year watching the broadcast has become a standard rite. This time was no exception. As expected, nothing supernatural was introduced, and the new OSX interface even left a touch of frustration. I had already fully intended to watch the remaining 15 minutes and go on my worldly affairs. But here, under the guise of the next update of Xcode, without the famous “one more thing”, unceremoniously, Craig Federigi represents a new programming language, whose name is Swift.

It was exactly what I was waiting for. How much I did not try to get used to the objc syntax, but many moments caused great inconvenience. It was like a fatal itch that periodically reminded of itself in the process of writing code. I remember that a couple of weeks before this very day, I dreamed of objc being more human and readable. I even came across an article on the subject about this , but considered the solution a crutch, which would only interfere more than bring benefits. And so, it was accomplished. I could not believe it. but the rest of the evening was definitely lost. All plans are relegated to the background. At first, the task was advanced rather to pull off a new Xcode and find out what this strange bird is. I played with the novelty until the moment when the kingdom of dreams did not beckon me to myself, but there was no longer any strength to resist. But stocking the book “The Swift Programming Language” has sworn itself that in the morning I’ll come close to learning all the subtleties of the swift brought to light.
')
No sooner said than done. I enthusiastically took up the task set to myself. I re-read the entire above-mentioned book and tried very hard to parse code samples from it in order to understand what was happening. And when it was finished, successfully owned the main structures and fell in love with this language. Simplified readable syntax, static typing, optional values, generics, and many other things Swift did not leave me indifferent. Going back to the old man objc absolutely no longer wanted (although still, probably, it will take a long time).

Part Two: Forward and with a song


So. The language teaching materials were learned, the fundamentals were disassembled, and we wanted to delve further into subtleties so much that a slightly rash decision was made to write the application in a language that was still in beta status.

It should be said that before that I had written only small programs for automating routine actions. Not to say that these were some kind of scripts. No, these were small applications on objc, using OOP, multithreading, graphical interfaces, and even some design patterns that still work successfully and daily simplify life. Only now they solve problems specific to me, and it is unlikely for this reason that someone will be very useful (although there is an idea to bring them to the appropriate state and put them on the page. Suddenly I am mistaken).

The motivation for all my offspring was one thought - if this thing doesn’t exist or is not implemented as I would like, and I know how I would like to see it, then I need to do it myself. This time I followed the same logic. For some time, the idea of ​​creating an application for the social network VKontakte under OSX was in my head, since the analogs did not suit me (I will not say what application it is, since it is still far from the release. . The project was coming much larger than what I usually did and demanded more labor and brainstorming than usual. I thought that this is a good opportunity to delve into the development of Swift, to also implement a pending idea. Moreover, one friend of mine was already familiar with the work of VK.API and also wanted to learn Swift. This inspired me even more.

And so, I began to try to build in my head a clearer concept of what I would like to see after the work. Only one, since my friend this idea, apparently, caught fire less. But with the API, he still helped me, for which a special thank you to him.

The first thing that came to mind was to find a library for working with VK.API for Objective-C (the benefit of Swift allows you to use objc code in your projects). This should greatly simplify the development, would not have to bother with the logic of requests / responses, but only to receive data, scatter them in the model and just as easy to send new data to the server.

Having rummaged in the official VK documentation I first of all paid attention to the official SDK . It was created for iOS, but can it be somehow screwed to OSX? It turned out not. In addition to the requests themselves, there is a lot that is tied to UIKit, and it was almost impossible to twist the nuts under AppKit. Desperate search in Google also did not give anything. All libraries were developed either exclusively under iOS, or were already some years ago, the author thrown on the dusty shelf subquality. No option came up to me. This, of course, saddened me, but since I took up the implementation of the idea, I did not stop. No library? Do without it!

Part Three: We will build our own house


The first thing to do was figure out the authorization process. It all turned out just because it passes through the oAuth protocol. At least log in and rip out the token with certain rights from the address bar of the browser to start trying to send test requests to the API turned out quickly. Then, which is a bit more complicated, you had to implement it in the application itself. WebView was created, which loaded the authorization page and its WebFrameLoadDelegate looked at the URL of the page and caught the token. At first, there were only three options for action: If there is a token in the URL, then pick it up and remove the WebView. If the user rejected the authorization, then we close the application altogether, and if you suddenly decide to go to some other page, then we don’t allow it to do so and return it to the authorization.

Requests I sent using NSURLConnection, and simultaneously. Parsil answers using NSJSONSerialization and as a result received such constructions:

(((jsonObject as? NSDictionary)?["phoneNumbers"] as? NSArray)?[0] as? NSDictionary)?["number"] as? NSString 

Agree, not the most pleasant sight. At this point, working with optional turns into hell. Read more about the problem here .

Somewhere here it began to become clear that everything that happens is not good and convenient. Plus, the API may return errors, authorization requests, authentication, captcha input. These things need to be foreseen and processed. After some deliberation, it was decided that it would be most logical to abstract all the logic of working with the API into a separate library. Firstly, it will allow not to smear the communication code with VK on the application, and secondly, it will be possible to reuse and eliminate errors more locally. Since there are a bunch of libraries for iOS and objc (although one official SDK is enough), I decided to make my own exclusively for Swift and OSX.

In general, it should be said that if I wrote on iOS, then such a problem would not have arisen, but since I am a desktop, or rather notebook, user and I love OSX more than iOS, then all the things that I do for myself - I do naturally under that platform, which I use more actively. Yes, and opportunities which, in my humble opinion, more. But the taste and color ...

Part Four: Development. Or a quick overview of SwiftyVK


The first structure appeared under the laconic name VK - the entry point, authorization, creation of requests and just a SwiftyVK conductor. At first, it had only methods for authorization and deauthorization + a draft delegate protocol that needs to be implemented in the class of the application using.

Next, it was necessary to implement a full authorization. The basis was taken already existing solution and brought to mind. When authorization application must transfer the rights that it wants to get. In the official SDK, the list of rights is transmitted as an array of strings. I decided to use the static typing of Swift and made an enumeration (of enum) from rights, which allows for authorization to transfer an array like this:

 [.messages,.offline,.friends,.wall,.photos,.audio,.video,.docs] 

Previously, the token received after authorization was stored as a variable until the program was closed. Now just needed to take care of its storage better. If you don’t go deep into details, then this is an object of the Token class that stores directly the string with the token, the logic of its return and the relevance check. If the token is outdated, then a new one will be requested. Token supports the NSCoding protocol and can be saved to NSUserDefaults or any other place to choose from. The object itself exists as a singleton, since we naturally do not need two tokens.

Authorization can be run in any window in the form of a sheet, or in a separate window. After passing the authorization, we begin to send API requests. It was necessary to implement the logic of sending and receiving a response. This is done by the Request / Connection, Connection, Response and NSURLFabric class / structure group. Outside the library itself, only the first is visible - Request and some of its properties / methods, which allow you to set up a request and send it to distant distances to VKontakte servers. Connection - the logic of sending a request, waiting for a response, handling errors and calling a callback. NSURLFabric is a factory with static methods that assembles an NSURLRequest configured and ready for dispatch from Request. Well, Response is a simple structure with the fields request, error, success and the logic of parsing the response from the server into an error or a successful answer.

Above it was shown how to parse JSON using NSJSONSerialization. To get more convenient and Swift-native methods for obtaining values, the library from the article in the same article describing this problem was first used, but since it still left many places where question marks were used during parsing , the transition to SwiftyJSON was made later. me, very convenient library. With its help, the problem was solved completely.

Errors are implemented in the Error class, which resembles NSError. Error objects can even be initialized with NSError, but more often it comes from JSON. Some errors are handled directly in SwiftyVK. For example, if the application sent a request that requires authorization, but we are not authorized, the error message will return, the authorization window will appear, and after passing it, the request will be redirected. Similarly with captcha. To disable this feature, you need to set the queryErrors query parameter to false. Then, even if a known SwiftyVK error is returned, the error handling block will be executed.

For a long time I suffered with synchronous and asynchronous requests. Not in terms of complexity of multithreading and asynchrony. There is nothing complicated. Problems were caused by sending synchronous requests in the main thread. Could get this situation - the application sends a synchronous request. The stream from which it was sent is blocked until the response or error block is called. The API returns an error that indicates the need for authorization / input captcha before calling the error block. The window needs to be shown from main thread, and it is already blocked waiting for a response. As a result, deadlock and hang tightly. How much I did not try to invent crutches to circumvent this — somewhere, something went wrong. Output - to synchronous requests with error checking from the main thread will work assert with the description that you should not send them. Of course, not the best way, but anything better than a deaf hang.

Since Swift is a strictly typed language, I wanted to strictly type everything so that there are as few places as possible to make mistakes in the future. Perhaps the most controversial for someone is to typify query arguments. In the official SDK, they are transmitted as a string. I decided to introduce the Arg: String intersection, in which I listed all the possible query arguments at the moment. In addition to minimizing errors, this allows auto-completion in Xcode to be used when specifying arguments.

For even more convenience, an API structure has been introduced, which by group-substructures contains methods for quickly creating a request to a specific API method. As a result, the creation of the request is something like this:

  let req = VK.API.Friends.get([ .count : "1", .fields : "city,domain" ]) 

As for me, very simple, readable and concise.

In addition to sending simple requests, it is possible to send requests with a custom method name (by passing it a string), calling execute, or remote procedures. Plus, the implementation of loading files (which most other libraries do not have) and LongPool client (this is not found anywhere).

Part Five. Last


Principle, this is all SwiftyVK consists of. For a long time I did not post it, because I tested it and brought it to mind. I think he is now ready to meet with the public. I tried to make the most simple and clear library for working with VK.API using the advantages of the new language. I would like to say that at the end of development I gave up on the idea of ​​doing it only under OSX and added equal support for iOS. I wanted to do more for tvOS, but there is no WebView there yet and it is impossible to log in using oAuth. As soon as possible - so soon. Under watchOS I decided not to do it, because I don’t know who might need it. I'm not even sure that this library may even be needed by anyone except me. But if I am mistaken and someone is interested in SwiftyVK, then thanks a lot and good luck with your use. Please write about all the wishes - we will do.

You can read the documentation in curved English and the source code here .

PS: If you have read this far, thank you so much for your time.

Source: https://habr.com/ru/post/269741/


All Articles