Perfect là gì ?
Perfect là một web-server và toolkit cho phép các nhà phát triển sử dụng ngôn ngữ lập trình Swift để xây dựng những ứng dụng và những REST service. Nó cho phép những developer có thể phát triển ứng dụng cả client-side và server-side trong cùng 1 workspace, cùng 1 ngôn ngữ lập trình Swift. Nó là một bộ khung hoàn hảo cho những kỹ thuật cloud và mobile.Tại sao lại sử dụng Perfect?
- Hiện tại Swift đã được Open Source vì thế nó sẽ còn phát triển nhanh và mạnh, do đó Swift là ngôn ngữ của tương lai. Nếu bạn học và nắm bắt cơ hội này chắc bạn sẽ có nhiều cơ hội phát triển sự nghiệp của mình trong tương lai hơn nữa.- Đa số những dân lập trình trên iOS chỉ biết về Objective-C hay Swift hoặc cả 2, vì thế nếu bạn muốn viết cho server-side thì bạn phải học thêm ngôn ngữ PHP hoặc Java để có thể viết cho server-side. Nếu bạn dùng thư viện Perfect thì bạn khỏi cần quan tâm đến việc học thêm ngôn ngữ khác cho công việc đó, bạn có thể dễ dàng debug cả bên client lẫn server đều được.
- Hiện tại Perfect cũng đang trong giai đoạn phát triển và đã có 1 phiên bản release là Perfect version 1.0. Code của thư viện Perfect cũng được open source trên GitHub, nếu các bạn muốn học thêm cách người ta viết library đó như thế nào thì cũng có thể tải về và đọc code.
- Swift và Perfect có thể chạy trên nền tảng Linux vì thế các bạn có thể viết để deploy trên Linux server. Hiện tại trên mạng cũng có nhiều hướng dẫn về việc này.
Làm thế nào thiết lập thư viện Perfect trên XCode?
Hiện tại mình chưa thấy đường link trên cocoapods cho thư viện này, vì thế cách mình hướng dẫn chỉ tải và dùng trên phiên bản release 1.0 như ở trên. Mình đang dùng OS X 10.11 và XCode 7.3 (7D175). Trên GitHub của Perfect nói phải như vậy mới build được thư viện của họ. Nên nếu ai đang xài phiên bản cũ hơn hãy update lên rồi mới chạy được nha.Demo này mình viết 1 ứng dụng trên iOS truy suất REST api, có trả về dữ liệu là 1 chuỗi HelloWorld theo dạng GET và POST:
- Bước 1: Bạn mở XCode, chọn New > Workspace để tạo 1 workspace mới, đặt tên workspace ví dụ như PerfectTutorials.
- Bước 2: Bạn tải version 1.0 của Perfect về và giải nén sẽ xuất hiện những thư mục như sau:
Bạn copy 2 thư mục là PerfectLib và PerfectServer vào cùng thư mục chứa workspace:
Quay trở lại XCode, bạn nhấn vào biểu tượng dấu cộng ở góc dưới bên trái, và chọn option là Add files to "PerfectTutorials", để thêm 2 project đó vào wordspace:
- Bước 3: Tạo 1 project trên iOS với tên là PerfectHelloWorld. Trong project đó bạn tạo 1 target mới với kiểu là 1 Cocoa framework cho OS X với tên là PerfectMyServer:
- Bước 4: Bạn phải link PerfectLib vào Framework này, chọn target PerfectMyServer, chọn General tab, chỗ Linked Frameworks and Libraries bạn chọn add PerfectLib cho OSX vào.
Sau đó bạn chuyển sang Build Settings tab, và thiết lập những thuộc tính sau:
- Skip Install = No
- Deployment Location = Yes
- Installation Directory = /PerfectLibraries
- Installation Build Products Location = $(CONFIGURATION_BUILD_DIR)
- Bước 5: Tại Group PerfectMyServer trên XCode bạn tạo 1 file mới với tên như PerfectHandlers trong file đó bạn viết những đoạn code như sau:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PerfectLib | |
// This function is required. The Perfect framework expects to find this function | |
// to do initialization | |
public func PerfectServerModuleInit() { | |
// Install the built-in routing handler. | |
// This is required by Perfect to initialize everything | |
Routing.Handler.registerGlobally() | |
// register a route for gettings posts | |
Routing.Routes["GET", "/hello"] = { _ in | |
return GetHelloWorldHandler() | |
} | |
// register a route for creating a new post | |
Routing.Routes["POST", "/hello"] = { _ in | |
return PostHelloWorldHandler() | |
} | |
} | |
class GetHelloWorldHandler: RequestHandler { | |
func handleRequest(request: WebRequest, response: WebResponse) { | |
do { | |
// encode the random content into JSON | |
let jsonEncoder = JSONEncoder() | |
let respString = try jsonEncoder.encode(["content": "[GET]Hello World!"]) | |
// write the JSON to the response body | |
response.appendBodyString(respString) | |
response.addHeader("Content-Type", value: "application/json") | |
response.setStatus(200, message: "OK") | |
} catch { | |
response.setStatus(400, message: "Bad Request") | |
} | |
response.requestCompletedCallback() | |
} | |
} | |
class PostHelloWorldHandler: RequestHandler { | |
func handleRequest(request: WebRequest, response: WebResponse) { | |
let reqData = request.postBodyString // get the request body | |
print(reqData) | |
let jsonDecoder = JSONDecoder() // JSON decoder | |
do { | |
// decode(_:) returns a JSONValue, which is just an alias to Any | |
// because we know the request will be a dictionary, we can just force | |
// the downcast to JSONDictionaryType. In a real production web service, | |
// you would include error checking here to validate the request that it is what | |
// we expect. For the purpose of this tutorial, we're gonna lean | |
// on the side of danger | |
let json = try jsonDecoder.decode(reqData) as! JSONDictionaryType | |
print("received request JSON: \(json.dictionary)") | |
let jsonEncoder = JSONEncoder() | |
let respString = try jsonEncoder.encode(["content": "[POST]Hello World!"]) | |
// write the JSON to the response body | |
response.appendBodyString(respString) | |
response.addHeader("Content-Type", value: "application/json") | |
response.setStatus(200, message: "OK") | |
} catch { | |
print("error decoding json from data: \(reqData)") | |
response.setStatus(400, message: "Bad Request") | |
} | |
// this completes the request and sends the response to the client | |
response.requestCompletedCallback() | |
} | |
} |
Bạn có thể thiết lập Scheme cho PerfectServerHTTPApp để mỗi lần build app này thì sẽ tự động build framework chứa code web-service của mình:
- Bước 6: Sau khi build Framework thành công bạn chọn Scheme là Perfect Server HTTP App và chọn run trên OS X:
Lưu ý: Theo mình thấy thì code trên Perfect Server HTTP App này thiếu 1 đoạn code để thay đổi đường dẫn Document Root. Các bạn có thể viết thêm 1 đoạn code trong file ViewController.swift của PerfectHTTPApp như hình sau:
- Khi bạn chạy ứng dụng PerfectServerHTTPApp thành công sẽ xuất hiện màn hình như sau:
Do mình đã thiết lập Server Address(mặc định là 0.0.0.0) theo ip của máy mình để những máy khác cùng lớp mạng có thể truy suất được và đổi Document Root (mặc định là ./webroot/) theo dúng thư mục webroot trên máy của mình. Ngoài ra các bạn nên để ý log trong console phải xuất ra như sau:
PS: Cách mở port để những máy khác có thể truy suất được máy mình làm local server các bạn vào Settings của hệ thống chọn Sharing và bật File Sharing lên:
Bạn đang chạy server thành công bây giờ là lúc bạn test xem server có phản hồi những request của bạn hay không thì bạn có thể gọi request bằng terminal, những addon trên browser hay viết ứng dụng để thử request lên server.
Gởi request lên server thông qua terminal:
Mở ứng dụng terminal và nhập đoạn lệnh sau để truyền request kiểu GET lên server:curl 10.190.201.95:8181/hello
Bạn sẽ thấy màn hình xuất hiện chữ như:Nhập đoạn lệnh sau để truyền request kiểu POST lên server:
curl -v -XPOST 10.190.201.95:8181/hello -H 'Content-Type: application/json' --data '{"content":"congpc"}'
Màn hình Terminal sẽ hiển thị như sau:Gởi request lên server bằng ứng dụng trên iOS dùng thư viện Alamofire:
Giả sử mình viết 1 ứng dụng có 2 nút button để gởi 2 loại request lên server với đoạn code như sau:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PCViewController: UIViewController { | |
@IBOutlet weak var requestGETButton: UIButton! | |
@IBOutlet weak var requestGETLabel: UILabel! | |
@IBOutlet weak var requestPOSTButton: UIButton! | |
@IBOutlet weak var requestPOSTLabel: UILabel! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view, typically from a nib. | |
} | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
// Dispose of any resources that can be recreated. | |
} | |
@IBAction func clickOnGETButton(sender: AnyObject) { | |
Alamofire.request(.GET, "http://10.190.201.95:8181/hello") | |
.responseJSON { response in | |
print(response.request) // original URL request | |
print(response.response) // URL response | |
print(response.data) // server data | |
print(response.result) // result of response serialization | |
if let JSON = response.result.value { | |
print("JSON: \(JSON)") | |
self.requestGETLabel.text = JSON.valueForKey("content") as? String | |
} | |
} | |
} | |
@IBAction func clickOnPOSTButton(sender: AnyObject) { | |
Alamofire.request(.POST, "http://10.190.201.95:8181/hello", parameters: ["content": "congpc.ios"], encoding: .JSON) | |
.responseJSON { response in | |
print(response.request) // original URL request | |
print(response.response) // URL response | |
print(response.data) // server data | |
print(response.result) // result of response serialization | |
if let JSON = response.result.value { | |
print("JSON: \(JSON)") | |
self.requestPOSTLabel.text = JSON.valueForKey("content") as? String | |
} | |
} | |
} | |
} |
Console sẽ sinh log như sau:
Thiết lập với cơ sở dữ liệu MySql:
- Bạn cần cài MySql với tại đường dẫn và làm theo hướng dẫn của trang MySql.- Bạn có thể tải MySql Workbench để thao tác trên GUI cho dễ.
- Tại Framework của server bạn thêm project MySql vào workspace và link framework đó vào Tutorials project:
Những database khác có thể làm tương tự như MongoDB, PostgreSQL.
Sau đó khai báo những thông số kết nối đến MySql, chi tiết mình có để trong code cũng như trên GitHub:
Các bạn có thể lấy toàn bộ source code của mình tại GitHub
Tài liệu tham khảo:
- Creating a Web Service for Your App in Swift with Perfect.- Integrate MySQL Into a Swift Perfect Web Server.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.