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.

549 lines
14 KiB

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