/MisskeyKit-for-iOS

An elegant Misskey framework written in Swift.

Primary LanguageSwiftMIT LicenseMIT

License Swift Version CocoaPods Compatible Carthage compatible

MisskeyKit for iOS

MisskeyKit is a framework for Misskey written in swift. You can call Misskey API intuitively. (日本語はこちら)


Dependencies


Contents



Installation

CocoaPods

You can use CocoaPods to install MisskeyKit by adding it to your Podfile:

pod 'MisskeyKit'

To get the full benefits, import MisskeyKit

import MisskeyKit

Manually

  1. Download and drop MisskeyKit in your project.
  2. Run carthage update.
  3. Congratulations!



How to use

Singleton

MisskeyKit adopts singleton pattern, because of keeping account information instead of developers.

So you always have to communicate with MisskeyKit via the following instances.

open class MisskeyKit {
  static public let auth: Auth
  static public var notes: Notes
  static public var users: Users
  static public var groups: Groups
  static public var lists: Lists
  static public var search: Search
  static public var notifications: Notifications
  static public var meta: Meta

How to change Misskey Instance

To change Misskey Instance, use MisskeyKit.changeInstance().

MisskeyKit.changeInstance(instance: "misskey.dev")

Authentication

There are 5 native steps for authentication.

  1. Access to Developer Center and Get Secret Key (aka appSecret).
  2. Get a Session Token.
  3. User authenticates via safari.
  4. Get an Access Token.
  5. Finally, Get an Api Key !

On the other hand, MisskeyKit is tooooo simple.

All you need is setup and present MisskeyKit.auth.viewController , which launchs browser for authentication and does tedious process instead of you.

Additionally, You can choose whether to use callback pattern or delegation pattern!


CallBack Pattern

MisskeyKit.auth.appSecret = "Enter your Secret Key"

let authVC = MisskeyKit.auth.viewController
authVC.resultApiKey() { apiKey in

    guard let apiKey = apiKey else { return }
    print(apiKey) // u can get uesr's apikey.

}

self.present(authVC, animated: true)

Delegation Pattern

class ViewController: UIViewController, AuthViewControllerDelegate {

  func something() {
      MisskeyKit.auth.appSecret = "Enter your Secret Key"

      let authVC = MisskeyKit.auth.viewController
      authVC.delegate = self

      self.present(authVC, animated: true)
  }

  //....

  func resultApiKey(_ apiKey: String?) { // Need: AuthViewControllerDelegate
      guard let apiKey = apiKey else { return }

      print(apiKey) // u can get uesr's apikey.
  }



Authentication (Advanced)

You can also call API of Authentication in the right order.

Get a Session Token

MisskeyKit.auth.startSession(appSecret: "Enter your appSecret") { auth, error in
    guard let auth = auth, let token = auth.token, error == nil else { /* Error */ return }

    print(token) // u got a Session Token.
}

After getting, you need to let your user authenticate via safari.

For example,

MisskeyKit.auth.startSession(appSecret: "Enter your appSecret") { auth, error in
    guard let auth = auth, let token = auth.token, error == nil else { /* Error */ return }

    print(token) // u got a Session Token.

    guard let url = URL(string: token.url) else { /* Error */ return }
    DispatchQueue.main.async {
      if UIApplication.shared.canOpenURL(url) {
        UIApplication.shared.open(url)
      }
    }
}

Get an Access Token

MisskeyKit.auth.getAccessToken() { auth, error in
    guard let auth = auth, error == nil else { return }

    print(auth.me) // u got a Session Token.
}

Get an Api Key

// If u get user's Access Token correctly, u can get Api key.
guard let apikey = MisskeyKit.auth.getAPIKey() else {

      /* Error */

}



Recycle Api Key

If wanting to recycle user's api key, you have to send it to MisskeyKit so use MisskeyKit.auth.setAPIKey().

MisskeyKit.auth.setAPIKey("Enter saved api key!")



How to call API

Look into my code of MisskeyKit and see how to describe.

Oh, it's too much hassle? Hmmm... Okay, I'll give you three examples.


For example, if you wanna post a note, check the following code.

(Once you get or set user's api key, you don't have to send Api key to each methods.)


 // Type of the first parameter "posts" will change according to purpose of methods you use.
 // In this method, type is NoteModel. You can see model class in "./MisskeyKit/APIs/Model".

 MisskeyKit.notes.createNote(text: "Enter some text!") { posts, error in  
            guard let posts = posts, error == nil else { /* Error */ return }

            // MisskeyKit.notes.createNote returns information of your post that you've just posted.
            // The fact itself that you receive it means that your request was accepted successfully.

            print(posts)
}

Second Example: If you wanna get one hundred notes from user's timeline, write like this code.

MisskeyKit.notes.getTimeline(limit: 100) { posts, error in
            guard let posts = posts, error == nil else { /* Error */ return }

            print(posts) // You can check 100 notes if your request was accepted successfully.
}

Final Example: MisskeyKit.drive.createFile , which is a method for "drive/create" api.

When using MisskeyKit.drive.createFile, you always have to add fileType. (fileType is expected to be MIME type.)

MisskeyKit.drive.createFile(fileData: targetImage, fileType: "image/jpeg", name: UUID().uuidString + ".jpeg", isSensitive: false, force: false) { result, error in
    guard let result = result, error == nil else { return }

    print(result.id)
}




Api-Method correspondence table

Misskey API MisskeyKit Methods
users/show Users.showUser
i Users.i
i/favorites Users.getAllFavorites
i/page-likes Users.getLikedPages
i/pages Users.getMyPages
i/update Users.updateMyAccount
i/pin Users.pin
i/unpin Users.unpin
following/create Users.follow
following/delete Users.unfollow
users/followers Users.getFollowers
users/following Users.getFollowing
users/get-frequently-replied-users Users.getFrequentlyRepliedUsers
users/relation Users.getUserRelationship
blocking/create Users.block
blocking/delete Users.unblock
blocking/list Users.getBlockingList
users/report-abuse Users.reportAsAbuse
users/recommendation Users.getUserRecommendation
following/requests/accept Users.acceptFollowRequest
following/requests/cancel Users.cancelFollowRequest
following/requests/reject Users.rejectFollowRequest
notes Notes.getAllNotes
notes/show Notes.showNote
notes/conversation Notes.getConversation, Notes.getChildren
users/notes Notes.getUserNotes
notes/mentions Notes.getMentionsForMe
notes/timeline Notes.getTimeline
notes/global-timeline Notes.getGlobalTimeline
notes/hybrid-timeline Notes.getHybridTimeline
notes/local-timeline Notes.getLocalTimeline
notes/user-list-timeline Notes.getUserListTimeline
notes/featured Notes.getFeatured
notes/create Notes.createNote, Notes.renote
notes/delete Notes.deletePost
notes/favorites/create Notes.createFavorite
notes/favorites/delete Notes.deleteFavorite
notes/reactions Notes.getReactions
notes/reactions/create Notes.createReaction
notes/reactions/delete Notes.deleteReaction
notes/renotes Notes.getRenotes
notes/unrenote Notes.unrenote
notes/replies Notes.getReplies
notes/watching/create Notes.watchNote
notes/watching/delete Notes.unWatchNote
i/read-all-unread-notes Notes.readAllUnreadNotes
notes/polls/vote Notes.vote
auth/session/generate Auth.startSession
meta Meta.get
users/groups/invitations/accept Groups.acceptInvitation
users/groups/invitations/reject Groups.rejectInvitation
users/groups/invite Groups.invite
users/groups/pull Groups.pullUser
users/groups/transfer Groups.transferUser
mute/create Mute.create
mute/delete Mute.delete
mute/list Mute.getList
drive/files/attached-notes Drive.getAttachedNotes
drive/files/delete Drive.deleteFile
drive/files/update Drive.updateFile
drive/files/upload-from-url Drive.uploadFileFromUrl
drive/folders/delete Drive.deleteFolder
drive/folders/update Drive.updateFolder
users/lists/pull Lists.pullUser
users/lists/push Lists.pushUser
users/lists/create Lists.create
users/lists/delete Lists.delete
users/lists/show Lists.show
users/lists/list Lists.getMyLists
users/lists/update Lists.update
i/read-all-messaging-messages Messaging.readAllMessaging
messaging/history Messaging.getHistory
messaging/messages Messaging.getMessageWithUser, Messaging.create
messaging/messages/delete Messaging.delete
messaging/messages/read Messaging.read
users/search Search.user
notes/search Search.notes
notes/search-by-tag Search.notesByTag
i/notifications Notificaitons.get
notifications/mark-all-as-read Notificaitons.markAllAsRead



Emojis

Misskey Instances have their own custom emojis and user can use them for reactions and posts.

Sometimes, however, data of user's posts(notes) and reactions that Misskey server sent to us don't contain information of custom emojis user used.

Moreover, if you want to develop stuff like emoji pickers, you have to get default and custom emojis data.

So MisskeyKit provides some methods for getting default / custom emojis data.

MisskeyKit.Emojis.getDefault{ result in
guard let result = result else { /* Error */ return }

   dump(result) // you can see information of default emojis
}
MisskeyKit.Emojis.getCustom{ result in
guard let result = result else { /* Error */ return }

   dump(result) // you can see information of custom emojis
}

Once you get information of emojis from Misskey Instance server, MisskeyKit keeps the data unless user killing your app.

Hence you don't have to communicate with Misskey Instance Server many times and overhead will be reduced.



Streaming API

MisskeyKit also provides wrapper of a streaming API as well as REST API!

(Streaming API is a subscription mechanism for binding client to server so that you can receive events in near real time.)


Streaming API adopts not HTTP protocol but WebSocket, so you need to connect to server by other methods.

However it's so easy to connect via WebSocket by MisskeyKit !


MisskeyKit.Streaming.connect()

All you have to do is just use MisskeyKit.Streaming.connect() !

(MisskeyKit.Streaming does not provide singleton instance, so you have to generate instance yourself.)

guard let apiKey = MisskeyKit.auth.getAPIKey() else { return }

let streaming = MisskeyKit.Streaming() // u have to generate instance yourself.
streaming.connect(apiKey: apiKey, channels: [.main, .homeTimeline]) { response, channel, type, error in

        // Do something ...

        //apiKey: Your Api Key.
        //channels: [SentStreamModel.Channel] Type / channels which you wanna connect to.

        //This closure captures and sends you events through channels which you subscribed.
        //response: Any? Type / events itself. You have to cast it according to type(third params of callback).
        //channel: SentStreamModel.Channel? Type / shows which channel sent events.
        //type: String? Type / shows what kind of events was sent. You'll use it to cast response.
        //error: Error? Type / If something wrong happens, error is sent

}



MisskeyKit.Streaming.captureNote()

Even if you use MisskeyKit.Streaming.connect() and listen to events, there are some notes you cannot receive.

For these notes, you have to call API that provides you capturing functions.(Click here for details.)


If you wanna capture some notes, use MisskeyKit.Streaming.captureNote()

do {
  try streaming.captureNote(noteId: "Enter note Id.")
}
catch {
   /* Error */
}

Once you capture a note, each events related to the note will sent to your callback method of MisskeyKit.streaming.connect().



MisskeyKit.Streaming.isConnected

This variable enables us to check whether streaming is connected now.

guard streaming.isConnected else { return }

// Good.



MisskeyKit.Streaming.stopListening()

If you want to disconnect specific channel, use MisskeyKit.Streaming.stopListening().

streaming.stopListening(channnel: SentStreamModel.Channel)
streaming.stopListening(channnels: [SentStreamModel.Channel])
streaming.stopListening(noteId: String)
streaming.stopListening(noteIds: [String])



MisskeyKitError

MisskeyKit has own Error enumeration so that we could handle some error flexibility.

public enum MisskeyKitError: Error {

    //These Error are corresponded to error codes sent by Misskey server.

    //400
    case ClientError

    //401
    case AuthenticationError

    //403
    case ForbiddonError

    //418
    case ImAI

    //429
    case TooManyError

    //500
    case InternalServerError



    //These Error are related to internal error.

    case CannotConnectStream

    case NoStreamConnection

    case FailedToDecodeJson

    case FailedToCommunicateWithServer

    case UnknownTypeResponse

    case ResponseIsNull
}



Contribute

We would love you for the contribution to MisskeyKit, check the LICENSE file for more info.

Others

Yuiga Wada - WebSite Twitter - @YuigaWada

Distributed under the MIT license. See LICENSE for more information.

https://github.com/YuigaWada/MisskeyKit-for-iOS