[1]
import Alamofire
public struct DesignerNewsService {
// Designer News API Doc: http://developers.news.layervault.com
// V2: https://github.com/metalabdesign/dn_api_v2
private static let baseURL = "https://www.designernews.com"
private static let clientID = "750ab22aac78be1c6d4bbe584f0e3477064f646720f327c5464bc127100a1a6d"
private static let clientSecret = "53e3822c49287190768e009a8f8e55d09041c5bf26d0ef982693f215c72d87da"
private enum ResourcePath: Printable {
case Login
case Stories
case StoryUpvote(storyId: Int)
case StoryReply(storyId: Int)
case CommentUpvote(commentId: Int)
case CommentReply(commentId: Int)
var description: String {
switch self {
case .Login: return "/oauth/token"
case .Stories: return "/api/v1/stories"
case .StoryUpvote(let id): return "/api/v1/stories/\(id)/upvote"
case .StoryReply(let id): return "/api/v1/stories/\(id)/reply"
case .CommentUpvote(let id): return "/api/v1/comments/\(id)/upvote"
case .CommentReply(let id): return "/api/v1/comments/\(id)/reply"
}
}
}
public enum StorySection: Printable {
case Default
case Recent
case Search(query: String)
public var description : String {
switch (self) {
case .Default: return ""
case .Recent: return "recent"
case .Search(_): return "search"
}
}
}
public static func storiesForSection(section: StorySection, page: Int, response: ([Story]) -> ()) {
let urlString = baseURL + ResourcePath.Stories.description + "/" + section.description
var parameters : [String:AnyObject] = [
"page": toString(page),
"client_id": clientID
]
let query: String?
switch (section) {
case let .Search(keyword):
query = keyword
default:
query = nil
}
parameters["query"] = query
Alamofire.request(.GET, urlString, parameters: parameters).response { (request, res, data, error) in
let stories = JSONParser.parseStories(data as? NSData)
response(stories)
}
}
public static func loginWithEmail(email: String, password: String, response: (token: String?) -> ()) {
let urlString = baseURL + ResourcePath.Login.description
let parameters = [
"grant_type": "password",
"username": email,
"password": password,
"client_id": clientID,
"client_secret": clientSecret
]
Alamofire.request(.POST, urlString, parameters: parameters)
.responseJSON { (_, _, json, _) in
let responseDictionary = json as? NSDictionary
let token = responseDictionary?["access_token"] as? String
response(token: token)
}
}
public static func upvoteStoryWithId(storyId: Int, token: String, response: (successful: Bool) -> ()) {
let resourcePath = ResourcePath.StoryUpvote(storyId: storyId)
upvoteWithResourcePath(resourcePath, token: token, response: response)
}
public static func upvoteCommentWithId(commentId: Int, token: String, response: (successful: Bool) -> ()) {
let resourcePath = ResourcePath.CommentUpvote(commentId: commentId)
upvoteWithResourcePath(resourcePath, token: token, response: response)
}
public static func replyStoryWithId(storyId: Int, token: String, body: String, response: (comment: Comment?, error: Error?) -> ()) {
let resourcePath = ResourcePath.StoryReply(storyId: storyId)
replyWithResourcePath(resourcePath, token: token, body: body, response: response)
}
public static func replyCommentWithId(commentId: Int, token: String, body: String, response: (comment: Comment?, error: Error?) -> ()) {
let resourcePath = ResourcePath.CommentReply(commentId: commentId)
replyWithResourcePath(resourcePath, token: token, body: body, response: response)
}
// MARK: Private Methods
private static func upvoteWithResourcePath(path: ResourcePath, token: String, response: (successful: Bool) -> ()) {
let urlString = baseURL + path.description
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
Alamofire.request(request).responseJSON { (_, urlResponse, _, _) in
let successful = urlResponse?.statusCode == 200
response(successful: successful)
}
}
private static func replyWithResourcePath(path: ResourcePath, token: String, body: String, response: (comment: Comment?, error: Error?) -> ()) {
let urlString = baseURL + path.description
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.HTTPBody = "comment[body]=\(body)".dataUsingEncoding(NSUTF8StringEncoding)
Alamofire.request(request).responseJSON { (_, urlResponse, json, error) in
if let message = json?["error"] as? String {
response(comment: nil, error: Error(message: message, code: urlResponse?.statusCode ?? 0))
} else if let commentDict = json?["comment"] as? NSDictionary {
let comment = JSONParser.parseComment(commentDict)
response(comment: comment, error: nil)
} else {
response(comment: nil, error: Error(message: error?.localizedDescription ?? "Something went wrong", code: error?.code ?? 0))
}
}
}
}
[2] 登陆按钮按下 调用接口
// MARK: Button
@IBAction func signupButtonPressed(sender: AnyObject) {
view.showLoading()
DesignerNewsService.loginWithEmail(emailTextField.text, password: passwordTextField.text) { token in
self.view.hideLoading()
if let token = token {
LocalStore.setAccessToken(token)
self.dialogView.animation = "zoomOut"
self.dialogView.animate()
self.dismissViewControllerAnimated(true, completion: nil)
UIApplication.sharedApplication().sendAction("reset:", to: nil, from: self, forEvent: nil)
self.delegate?.loginViewControllerDidLogin(self)
} else {
self.dialogView.animation = "shake"
self.dialogView.animate()
}
}
}
[3] 加载本地数据
//
// LocalStore.swift
//
import UIKit
struct LocalStore {
private static let visitedStoriesKey = "visitedStoriesKey"
private static let upvotedStoriesKey = "upvotedStoriesKey"
private static let repliedStoriesKey = "repliedStoriesKey"
private static let upvotedCommentsKey = "upvotedCommentsKey"
private static let accessTokenKey = "accessTokenKey"
private static let userDefaults = NSUserDefaults.standardUserDefaults()
static func setIntroAsVisited() {
userDefaults.setObject(true, forKey: "introKey")
}
static func isIntroVisited() -> Bool {
return userDefaults.boolForKey("introKey")
}
static func setStoryAsReplied(storyId: Int) {
appendId(storyId, toKey: repliedStoriesKey)
}
static func setStoryAsVisited(storyId: Int) {
appendId(storyId, toKey: visitedStoriesKey)
}
static func setStoryAsUpvoted(storyId: Int) {
appendId(storyId, toKey: upvotedStoriesKey)
}
static func removeStoryFromUpvoted(storyId: Int) {
removeId(storyId, forKey: upvotedStoriesKey)
}
static func setCommentAsUpvoted(commentId: Int) {
appendId(commentId, toKey: upvotedCommentsKey)
}
static func removeCommentFromUpvoted(commentId: Int) {
removeId(commentId, forKey: upvotedCommentsKey)
}
static func isStoryReplied(storyId: Int) -> Bool {
return arrayForKey(repliedStoriesKey, containsId: storyId)
}
static func isStoryVisited(storyId: Int) -> Bool {
return arrayForKey(visitedStoriesKey, containsId: storyId)
}
static func isStoryUpvoted(storyId: Int) -> Bool {
return arrayForKey(upvotedStoriesKey, containsId: storyId)
}
static func isCommentUpvoted(commentId: Int) -> Bool {
return arrayForKey(upvotedCommentsKey, containsId: commentId)
}
static func setAccessToken(token: String) {
userDefaults.setObject(token, forKey: accessTokenKey)
userDefaults.synchronize()
}
private static func deleteAccessToken() {
userDefaults.removeObjectForKey(accessTokenKey)
userDefaults.synchronize()
}
static func removeUpvotes() {
userDefaults.removeObjectForKey(upvotedStoriesKey)
userDefaults.removeObjectForKey(upvotedCommentsKey)
userDefaults.synchronize()
}
static func accessToken() -> String? {
return userDefaults.stringForKey(accessTokenKey)
}
static func logout() {
self.deleteAccessToken()
}
// MARK: Helper
static private func arrayForKey(key: String, containsId id: Int) -> Bool {
let elements = userDefaults.arrayForKey(key) as? [Int] ?? []
return contains(elements, id)
}
static private func appendId(id: Int, toKey key: String) {
let elements = userDefaults.arrayForKey(key) as? [Int] ?? []
if !contains(elements, id) {
userDefaults.setObject(elements + [id], forKey: key)
userDefaults.synchronize()
}
}
static private func removeId(id: Int, forKey key: String) {
var elements = userDefaults.arrayForKey(key) as? [Int] ?? []
if let index = find(elements, id) {
elements.removeAtIndex(index)
userDefaults.setObject(elements, forKey: key)
userDefaults.synchronize()
}
}
}
[4]将数据存储在本地
//
// StoriesLoader.swift
//
import Foundation
import DesignerNewsKit
class StoriesLoader {
typealias StoriesLoaderCompletion = (stories:[Story]) ->()
private (set) var hasMore : Bool = false
private var page : Int = 1
private var isLoading : Bool = false
private let section : DesignerNewsService.StorySection
init(_ section: DesignerNewsService.StorySection = .Default) {
self.section = section
}
func load(page: Int = 1, completion: StoriesLoaderCompletion) {
if isLoading {
return
}
isLoading = true
DesignerNewsService.storiesForSection(section, page: page) { [weak self] stories in
if let strongSelf = self {
switch (strongSelf.section) {
case .Search(_):
strongSelf.hasMore = false
default:
strongSelf.hasMore = stories.count > 0
}
strongSelf.isLoading = false
completion(stories: stories)
}
}
}
func next(completion: (stories:[Story]) ->()) {
if isLoading {
return
}
++page
load(page: page, completion: completion)
}
}