You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

562 lines
14 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. # SwiftyJSON
  2. [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![CocoaPods](https://img.shields.io/cocoapods/v/SwiftyJSON.svg) ![Platform](https://img.shields.io/badge/platforms-iOS%208.0%20%7C%20macOS%2010.10%20%7C%20tvOS%209.0%20%7C%20watchOS%203.0-F28D00.svg) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
  3. SwiftyJSON makes it easy to deal with JSON data in Swift.
  4. Platform | Build Status
  5. ---------| --------------|
  6. *OS | [![Travis CI](https://travis-ci.org/SwiftyJSON/SwiftyJSON.svg?branch=master)](https://travis-ci.org/SwiftyJSON/SwiftyJSON) |
  7. [Linux](https://github.com/IBM-Swift/SwiftyJSON) | [![Build Status](https://travis-ci.org/IBM-Swift/SwiftyJSON.svg?branch=master)](https://travis-ci.org/IBM-Swift/SwiftyJSON) |
  8. 1. [Why is the typical JSON handling in Swift NOT good](#why-is-the-typical-json-handling-in-swift-not-good)
  9. 2. [Requirements](#requirements)
  10. 3. [Integration](#integration)
  11. 4. [Usage](#usage)
  12. - [Initialization](#initialization)
  13. - [Subscript](#subscript)
  14. - [Loop](#loop)
  15. - [Error](#error)
  16. - [Optional getter](#optional-getter)
  17. - [Non-optional getter](#non-optional-getter)
  18. - [Setter](#setter)
  19. - [Raw object](#raw-object)
  20. - [Literal convertibles](#literal-convertibles)
  21. - [Merging](#merging)
  22. 5. [Work with Alamofire](#work-with-alamofire)
  23. 6. [Work with Moya](#work-with-moya)
  24. 7. [SwiftyJSON Model Generator](#swiftyjson-model-generator)
  25. > [中文介绍](http://tangplin.github.io/swiftyjson/)
  26. ## Why is the typical JSON handling in Swift NOT good?
  27. Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types.
  28. Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to [Twitter's API](https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-home_timeline)).
  29. The code would look like this:
  30. ```swift
  31. if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
  32. let user = statusesArray[0]["user"] as? [String: Any],
  33. let username = user["name"] as? String {
  34. // Finally we got the username
  35. }
  36. ```
  37. It's not good.
  38. Even if we use optional chaining, it would be messy:
  39. ```swift
  40. if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
  41. let username = (JSONObject[0]["user"] as? [String: Any])?["name"] as? String {
  42. // There's our username
  43. }
  44. ```
  45. An unreadable mess--for something that should really be simple!
  46. With SwiftyJSON all you have to do is:
  47. ```swift
  48. let json = JSON(data: dataFromNetworking)
  49. if let userName = json[0]["user"]["name"].string {
  50. //Now you got your value
  51. }
  52. ```
  53. And don't worry about the Optional Wrapping thing. It's done for you automatically.
  54. ```swift
  55. let json = JSON(data: dataFromNetworking)
  56. let result = json[999999]["wrong_key"]["wrong_name"]
  57. if let userName = result.string {
  58. //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety
  59. } else {
  60. //Print the error
  61. print(result.error)
  62. }
  63. ```
  64. ## Requirements
  65. - iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+
  66. - Xcode 8
  67. ## Integration
  68. #### CocoaPods (iOS 8+, OS X 10.9+)
  69. You can use [CocoaPods](http://cocoapods.org/) to install `SwiftyJSON` by adding it to your `Podfile`:
  70. ```ruby
  71. platform :ios, '8.0'
  72. use_frameworks!
  73. target 'MyApp' do
  74. pod 'SwiftyJSON', '~> 4.0'
  75. end
  76. ```
  77. #### Carthage (iOS 8+, OS X 10.9+)
  78. You can use [Carthage](https://github.com/Carthage/Carthage) to install `SwiftyJSON` by adding it to your `Cartfile`:
  79. ```
  80. github "SwiftyJSON/SwiftyJSON" ~> 4.0
  81. ```
  82. If you use Carthage to build your dependencies, make sure you have added `SwiftyJSON.framework` to the "Linked Frameworks and Libraries" section of your target, and have included them in your Carthage framework copying build phase.
  83. #### Swift Package Manager
  84. You can use [The Swift Package Manager](https://swift.org/package-manager) to install `SwiftyJSON` by adding the proper description to your `Package.swift` file:
  85. ```swift
  86. // swift-tools-version:4.0
  87. import PackageDescription
  88. let package = Package(
  89. name: "YOUR_PROJECT_NAME",
  90. dependencies: [
  91. .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
  92. ]
  93. )
  94. ```
  95. Then run `swift build` whenever you get prepared.
  96. #### Manually (iOS 7+, OS X 10.9+)
  97. To use this library in your project manually you may:
  98. 1. for Projects, just drag SwiftyJSON.swift to the project tree
  99. 2. for Workspaces, include the whole SwiftyJSON.xcodeproj
  100. ## Usage
  101. #### Initialization
  102. ```swift
  103. import SwiftyJSON
  104. ```
  105. ```swift
  106. let json = JSON(data: dataFromNetworking)
  107. ```
  108. Or
  109. ```swift
  110. let json = JSON(jsonObject)
  111. ```
  112. Or
  113. ```swift
  114. if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) {
  115. let json = JSON(data: dataFromString)
  116. }
  117. ```
  118. #### Subscript
  119. ```swift
  120. // Getting a double from a JSON Array
  121. let name = json[0].double
  122. ```
  123. ```swift
  124. // Getting an array of string from a JSON Array
  125. let arrayNames = json["users"].arrayValue.map {$0["name"].stringValue}
  126. ```
  127. ```swift
  128. // Getting a string from a JSON Dictionary
  129. let name = json["name"].stringValue
  130. ```
  131. ```swift
  132. // Getting a string using a path to the element
  133. let path: [JSONSubscriptType] = [1,"list",2,"name"]
  134. let name = json[path].string
  135. // Just the same
  136. let name = json[1]["list"][2]["name"].string
  137. // Alternatively
  138. let name = json[1,"list",2,"name"].string
  139. ```
  140. ```swift
  141. // With a hard way
  142. let name = json[].string
  143. ```
  144. ```swift
  145. // With a custom way
  146. let keys:[JSONSubscriptType] = [1,"list",2,"name"]
  147. let name = json[keys].string
  148. ```
  149. #### Loop
  150. ```swift
  151. // If json is .Dictionary
  152. for (key,subJson):(String, JSON) in json {
  153. // Do something you want
  154. }
  155. ```
  156. *The first element is always a String, even if the JSON is an Array*
  157. ```swift
  158. // If json is .Array
  159. // The `index` is 0..<json.count's string value
  160. for (index,subJson):(String, JSON) in json {
  161. // Do something you want
  162. }
  163. ```
  164. #### Error
  165. ##### SwiftyJSON 4.x
  166. SwiftyJSON 4.x introduces an enum type called `SwiftyJSONError`, which includes `unsupportedType`, `indexOutOfBounds`, `elementTooDeep`, `wrongType`, `notExist` and `invalidJSON`, at the same time, `ErrorDomain` are being replaced by `SwiftyJSONError.errorDomain`.
  167. Note: Those old error types are deprecated in SwiftyJSON 4.x and will be removed in the future release.
  168. ##### SwiftyJSON 3.x
  169. Use a subscript to get/set a value in an Array or Dictionary
  170. If the JSON is:
  171. * an array, the app may crash with "index out-of-bounds."
  172. * a dictionary, it will be assigned to `nil` without a reason.
  173. * not an array or a dictionary, the app may crash with an "unrecognised selector" exception.
  174. This will never happen in SwiftyJSON.
  175. ```swift
  176. let json = JSON(["name", "age"])
  177. if let name = json[999].string {
  178. // Do something you want
  179. } else {
  180. print(json[999].error!) // "Array[999] is out of bounds"
  181. }
  182. ```
  183. ```swift
  184. let json = JSON(["name":"Jack", "age": 25])
  185. if let name = json["address"].string {
  186. // Do something you want
  187. } else {
  188. print(json["address"].error!) // "Dictionary["address"] does not exist"
  189. }
  190. ```
  191. ```swift
  192. let json = JSON(12345)
  193. if let age = json[0].string {
  194. // Do something you want
  195. } else {
  196. print(json[0]) // "Array[0] failure, It is not an array"
  197. print(json[0].error!) // "Array[0] failure, It is not an array"
  198. }
  199. if let name = json["name"].string {
  200. // Do something you want
  201. } else {
  202. print(json["name"]) // "Dictionary[\"name"] failure, It is not an dictionary"
  203. print(json["name"].error!) // "Dictionary[\"name"] failure, It is not an dictionary"
  204. }
  205. ```
  206. #### Optional getter
  207. ```swift
  208. // NSNumber
  209. if let id = json["user"]["favourites_count"].number {
  210. // Do something you want
  211. } else {
  212. // Print the error
  213. print(json["user"]["favourites_count"].error!)
  214. }
  215. ```
  216. ```swift
  217. // String
  218. if let id = json["user"]["name"].string {
  219. // Do something you want
  220. } else {
  221. // Print the error
  222. print(json["user"]["name"].error!)
  223. }
  224. ```
  225. ```swift
  226. // Bool
  227. if let id = json["user"]["is_translator"].bool {
  228. // Do something you want
  229. } else {
  230. // Print the error
  231. print(json["user"]["is_translator"].error!)
  232. }
  233. ```
  234. ```swift
  235. // Int
  236. if let id = json["user"]["id"].int {
  237. // Do something you want
  238. } else {
  239. // Print the error
  240. print(json["user"]["id"].error!)
  241. }
  242. ...
  243. ```
  244. #### Non-optional getter
  245. Non-optional getter is named `xxxValue`
  246. ```swift
  247. // If not a Number or nil, return 0
  248. let id: Int = json["id"].intValue
  249. ```
  250. ```swift
  251. // If not a String or nil, return ""
  252. let name: String = json["name"].stringValue
  253. ```
  254. ```swift
  255. // If not an Array or nil, return []
  256. let list: Array<JSON> = json["list"].arrayValue
  257. ```
  258. ```swift
  259. // If not a Dictionary or nil, return [:]
  260. let user: Dictionary<String, JSON> = json["user"].dictionaryValue
  261. ```
  262. #### Setter
  263. ```swift
  264. json["name"] = JSON("new-name")
  265. json[0] = JSON(1)
  266. ```
  267. ```swift
  268. json["id"].int = 1234567890
  269. json["coordinate"].double = 8766.766
  270. json["name"].string = "Jack"
  271. json.arrayObject = [1,2,3,4]
  272. json.dictionaryObject = ["name":"Jack", "age":25]
  273. ```
  274. #### Raw object
  275. ```swift
  276. let rawObject: Any = json.object
  277. ```
  278. ```swift
  279. let rawValue: Any = json.rawValue
  280. ```
  281. ```swift
  282. //convert the JSON to raw NSData
  283. do {
  284. let rawData = try json.rawData()
  285. //Do something you want
  286. } catch {
  287. print("Error \(error)")
  288. }
  289. ```
  290. ```swift
  291. //convert the JSON to a raw String
  292. if let rawString = json.rawString() {
  293. //Do something you want
  294. } else {
  295. print("json.rawString is nil")
  296. }
  297. ```
  298. #### Existence
  299. ```swift
  300. // shows you whether value specified in JSON or not
  301. if json["name"].exists()
  302. ```
  303. #### Literal convertibles
  304. For more info about literal convertibles: [Swift Literal Convertibles](http://nshipster.com/swift-literal-convertible/)
  305. ```swift
  306. // StringLiteralConvertible
  307. let json: JSON = "I'm a json"
  308. ```
  309. ```swift
  310. / /IntegerLiteralConvertible
  311. let json: JSON = 12345
  312. ```
  313. ```swift
  314. // BooleanLiteralConvertible
  315. let json: JSON = true
  316. ```
  317. ```swift
  318. // FloatLiteralConvertible
  319. let json: JSON = 2.8765
  320. ```
  321. ```swift
  322. // DictionaryLiteralConvertible
  323. let json: JSON = ["I":"am", "a":"json"]
  324. ```
  325. ```swift
  326. // ArrayLiteralConvertible
  327. let json: JSON = ["I", "am", "a", "json"]
  328. ```
  329. ```swift
  330. // With subscript in array
  331. var json: JSON = [1,2,3]
  332. json[0] = 100
  333. json[1] = 200
  334. json[2] = 300
  335. json[999] = 300 // Don't worry, nothing will happen
  336. ```
  337. ```swift
  338. // With subscript in dictionary
  339. var json: JSON = ["name": "Jack", "age": 25]
  340. json["name"] = "Mike"
  341. json["age"] = "25" // It's OK to set String
  342. json["address"] = "L.A." // Add the "address": "L.A." in json
  343. ```
  344. ```swift
  345. // Array & Dictionary
  346. var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]]
  347. json["list"][3]["what"] = "that"
  348. json["list",3,"what"] = "that"
  349. let path: [JSONSubscriptType] = ["list",3,"what"]
  350. json[path] = "that"
  351. ```
  352. ```swift
  353. // With other JSON objects
  354. let user: JSON = ["username" : "Steve", "password": "supersecurepassword"]
  355. let auth: JSON = [
  356. "user": user.object, // use user.object instead of just user
  357. "apikey": "supersecretapitoken"
  358. ]
  359. ```
  360. #### Merging
  361. It is possible to merge one JSON into another JSON. Merging a JSON into another JSON adds all non existing values to the original JSON which are only present in the `other` JSON.
  362. If both JSONs contain a value for the same key, _mostly_ this value gets overwritten in the original JSON, but there are two cases where it provides some special treatment:
  363. - In case of both values being a `JSON.Type.array` the values form the array found in the `other` JSON getting appended to the original JSON's array value.
  364. - In case of both values being a `JSON.Type.dictionary` both JSON-values are getting merged the same way the encapsulating JSON is merged.
  365. In case, where two fields in a JSON have a different types, the value will get always overwritten.
  366. There are two different fashions for merging: `merge` modifies the original JSON, whereas `merged` works non-destructively on a copy.
  367. ```swift
  368. let original: JSON = [
  369. "first_name": "John",
  370. "age": 20,
  371. "skills": ["Coding", "Reading"],
  372. "address": [
  373. "street": "Front St",
  374. "zip": "12345",
  375. ]
  376. ]
  377. let update: JSON = [
  378. "last_name": "Doe",
  379. "age": 21,
  380. "skills": ["Writing"],
  381. "address": [
  382. "zip": "12342",
  383. "city": "New York City"
  384. ]
  385. ]
  386. let updated = original.merge(with: update)
  387. // [
  388. // "first_name": "John",
  389. // "last_name": "Doe",
  390. // "age": 21,
  391. // "skills": ["Coding", "Reading", "Writing"],
  392. // "address": [
  393. // "street": "Front St",
  394. // "zip": "12342",
  395. // "city": "New York City"
  396. // ]
  397. // ]
  398. ```
  399. ## String representation
  400. There are two options available:
  401. - use the default Swift one
  402. - use a custom one that will handle optionals well and represent `nil` as `"null"`:
  403. ```swift
  404. let dict = ["1":2, "2":"two", "3": nil] as [String: Any?]
  405. let json = JSON(dict)
  406. let representation = json.rawString(options: [.castNilToNSNull: true])
  407. // representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null}
  408. ```
  409. ## Work with [Alamofire](https://github.com/Alamofire/Alamofire)
  410. SwiftyJSON nicely wraps the result of the Alamofire JSON response handler:
  411. ```swift
  412. Alamofire.request(url, method: .get).validate().responseJSON { response in
  413. switch response.result {
  414. case .success(let value):
  415. let json = JSON(value)
  416. print("JSON: \(json)")
  417. case .failure(let error):
  418. print(error)
  419. }
  420. }
  421. ```
  422. We also provide an extension of Alamofire for serializing NSData to SwiftyJSON's JSON.
  423. See: [Alamofire-SwiftyJSON](https://github.com/SwiftyJSON/Alamofire-SwiftyJSON)
  424. ## Work with [Moya](https://github.com/Moya/Moya)
  425. SwiftyJSON parse data to JSON:
  426. ```swift
  427. let provider = MoyaProvider<Backend>()
  428. provider.request(.showProducts) { result in
  429. switch result {
  430. case let .success(moyaResponse):
  431. let data = moyaResponse.data
  432. let json = JSON(data: data) // convert network data to json
  433. print(json)
  434. case let .failure(error):
  435. print("error: \(error)")
  436. }
  437. }
  438. ```
  439. ## SwiftyJSON Model Generator
  440. Tools to generate SwiftyJSON Models
  441. * [JSON Cafe](http://www.jsoncafe.com/)
  442. * [JSON Export](https://github.com/Ahmed-Ali/JSONExport)