Giter Site home page Giter Site logo

bustoutsolutions / siesta Goto Github PK

View Code? Open in Web Editor NEW
2.2K 53.0 159.0 14.53 MB

The civilized way to write REST API clients for iOS / macOS

Home Page: http://siestaframework.com

License: MIT License

Swift 96.64% Objective-C 2.30% Ruby 1.06%
swift ios macos networking rest caching observer reactive

siesta's Introduction

Siesta

The elegant way to write iOS / macOS REST clients

Build Status Version Platforms Docs GitHub license Twitter: @siestaframework

Drastically simplifies app code by providing a client-side cache of observable models for RESTful resources.

  • OS: iOS 10+, macOS 10.11+, tvOS 9.0+
  • Languages: Written in Swift, supports apps in both Swift and Objective-C
  • Tool requirements: Xcode 12+, Swift 5.3+ (See swift-* branches for legacy support)
  • License: MIT

Table of Contents

Overview

Documentation

  • User Guide — Extensive! Thrilling! Full of examples!
  • API Docs — Lascivious detail! Hidden treasure! More examples!
  • Specs — OK, doesn’t sound glamorous, but surprisingly informative.

What’s It For?

The Problem

Want your app to talk to a remote API? Welcome to your state nightmare!

You need to display response data whenever it arrives. Unless the requesting screen is no longer visible. Unless some other currently visible bit of UI happens to need the same data. Or is about to need it.

You should show a loading indicator (but watch out for race conditions that leave it stuck spinning forever), display user-friendly errors (but not redundantly — no modal alert dogpiles!), give users a retry mechanism … and hide all of that when a subsequent request succeeds.

Be sure to avoid redundant requests — and redundant response deserialization. Deserialization should be on a background thread, of course. Oh, and remember not to retain your ViewController / model / helper thingy by accident in your callback closures. Unless you’re supposed to.

Naturally you’ll want to rewrite all of this from scratch in a slightly different ad hoc way for every project you create.

What could possibly go wrong?

The Solution

Siesta ends this headache by providing a resource-centric alternative to the familiar request-centric approach.

Siesta provides an app-wide observable model of a RESTful resource’s state. This model answers three basic questions:

  • What is the latest data for this resource, if any?
  • Did the latest request result in an error?
  • Is there a request in progress?

…and broadcasts notifications whenever the answers to these questions change.

Siesta handles all the transitions and corner cases to deliver these answers wrapped up with a pretty bow on top, letting you focus on your logic and UI.

Features

  • Decouples view, model, and controller lifecycle from network request lifecycle
  • Decouples request initiation from request configuration
  • Eliminates error-prone state tracking logic
  • Eliminates redundant network requests
  • Unified handling for all errors: encoding, network, server-side, and parsing
  • Highly extensible, multithreaded response deserialization
  • Transparent built-in parsing (which you can turn off) for JSON, text, and images
  • Smooth progress reporting that accounts for upload, download, and latency
  • Transparent Etag / If-Modified-Since handling
  • Prebaked UI helpers for loading & error handling, remote images
  • Debug-friendly, customizable logging
  • Written in Swift with a great Swift-centric API, but…
  • …also works great from Objective-C thanks to a compatibility layer.
  • Lightweight. Won’t achieve sentience and attempt to destroy you.
  • Robust regression tests
  • Documentation and more documentation

What it doesn’t do

  • It doesn’t reinvent networking. Siesta delegates network operations to your library of choice (URLSession by default, or Alamofire, or inject your own custom adapter).
  • It doesn’t hide HTTP. On the contrary, Siesta strives to expose the full richness of HTTP while providing conveniences to simplify common usage patterns. You can devise an abstraction layer to suit your own particular needs, or work directly with Siesta’s nice APIs for requests and response entities.
  • It doesn’t do automatic response ↔ model mapping. This means that Siesta doesn’t constrain your response models, or force you to have any at all. Add a response transformer to output models of whatever flavor your app prefers, or work directly with parsed JSON.

Origin

This project started as helper code we wrote out of practical need on several Bust Out Solutions projects. When we found ourselves copying the code between projects, we knew it was time to open source it.

For the open source transition, we took the time to rewrite our code in Swift — and rethink it in Swift, embracing the language to make the API as clean as the concepts.

Siesta’s code is therefore both old and new: battle-tested on the App Store, then reincarnated in a Swifty green field.

Design Philosophy

Make the default thing the right thing most of the time.

Make the right thing easy all of the time.

Build from need. Don’t invent solutions in search of problems.

Design the API with these goals:

  1. Make client code easy to read.
  2. Make client code easy to write.
  3. Keep the API clean.
  4. Keep the implementation tidy.

…in that order of priority.


Installation

Siesta requires Swift 5.3+ and Xcode 12+. (Use the swift-* branches branches if you are still on an older version.)

Swift Package Manager

In Xcode:

  • File → Swift Packages → Add Package Dependency…
  • Enter https://github.com/bustoutsolutions/siesta in the URL field and click Next.
  • The defaults for the version settings are good for most projects. Click Next.
  • Check the checkbox next to “Siesta.”
    • Also check “SiestaUI” if you want to use any of the UI helpers.
    • Also check “Siesta_Alamofire” if you want to use the Alamofire extension for Siesta.
  • Click “Finish.”

Please note that Xcode will show all of Siesta’s optional and test-only dependencies, including Quick, Nimble, and Alamofire. Don’t worry: these won’t actually be bundled into your app (except Alamofire, if you use it).

CocoaPods

In your Podfile:

pod 'Siesta', '~> 1.0'

If you want to use the UI helpers:

pod 'Siesta/UI', '~> 1.0'

If you want to use Alamofire as your networking provider instead of Foundation’s URLSession:

pod 'Siesta/Alamofire', '~> 1.0'

(You’ll also need to pass an Alamofire.Manager when you configure your Siesta.Service. See the API docs for more info.)

Carthage

In your Cartfile:

github "bustoutsolutions/siesta" ~> 1.0

Follow the Carthage instructions to add Siesta.framework to your project. If you want to use the UI helpers, you will also need to add SiestaUI.framework to your project as well.

As of this writing, there is one additional step you need to follow that isn’t in the Carthage docs:

  • Build settings → Framework search paths → $(PROJECT_DIR)/Carthage/Build/iOS/

(In-depth discussion of Carthage in recent Xcode versions is here.)

The code in Extensions/ is not part of the Siesta.framework that Carthage builds. (This includes optional integrations for other libraries, such as Alamofire.) You will need to include those source files in your project manually if you want to use them.

Git Submodule

  1. Clone Siesta as a submodule into the directory of your choice, in this case Libraries/Siesta:

    git submodule add https://github.com/bustoutsolutions/siesta.git Libraries/Siesta
    git submodule update --init
    
  2. Drag Siesta.xcodeproj into your project tree as a subproject.

  3. Under your project's Build Phases, expand Target Dependencies. Click the + button and add Siesta.

  4. Expand the Link Binary With Libraries phase. Click the + button and add Siesta.

  5. Click the + button in the top left corner to add a Copy Files build phase. Set the directory to Frameworks. Click the + button and add Siesta.

If you want to use the UI helpers, you will need to repeat steps 3–5 for SiestaUI.

Installation troubles?

Please let us know about it, even if you eventually figure it out. Knowing where people get stuck will help improve these instructions!


Basic Usage

Make a shared service instance for the REST API you want to use:

let MyAPI = Service(baseURL: "https://api.example.com")

Now register your view controller — or view, internal glue class, reactive signal/sequence, anything you like — to receive notifications whenever a particular resource’s state changes:

override func viewDidLoad() {
    super.viewDidLoad()

    MyAPI.resource("/profile").addObserver(self)
}

Use those notifications to populate your UI:

func resourceChanged(_ resource: Resource, event: ResourceEvent) {
    nameLabel.text = resource.jsonDict["name"] as? String
    colorLabel.text = resource.jsonDict["favoriteColor"] as? String

    errorLabel.text = resource.latestError?.userMessage
}

Or if you don’t like delegates, Siesta supports closure observers:

MyAPI.resource("/profile").addObserver(owner: self) {
    [weak self] resource, _ in

    self?.nameLabel.text = resource.jsonDict["name"] as? String
    self?.colorLabel.text = resource.jsonDict["favoriteColor"] as? String

    self?.errorLabel.text = resource.latestError?.userMessage
}

Note that no actual JSON parsing occurs when we invoke jsonDict. The JSON has already been parsed off the main thread, in a GCD queue — and unlike other frameworks, it is only parsed once no matter how many observers there are.

Of course, you probably don’t want to work with raw JSON in all your controllers. You can configure Siesta to automatically turn raw responses into models:

MyAPI.configureTransformer("/profile") {  // Path supports wildcards
    UserProfile(json: $0.content)         // Create models however you like
}

…and now your observers see models instead of JSON:

MyAPI.resource("/profile").addObserver(owner: self) {
    [weak self] resource, _ in
    self?.showProfile(resource.typedContent())  // Response now contains UserProfile instead of JSON
}

func showProfile(profile: UserProfile?) {
    ...
}

Trigger a staleness-aware, redundant-request-suppressing load when the view appears:

override func viewWillAppear(_ animated: Bool) {
    MyAPI.resource("/profile").loadIfNeeded()
}

…and you have a networked UI.

Add a loading indicator:

MyAPI.resource("/profile").addObserver(owner: self) {
    [weak self] resource, event in

    self?.activityIndicator.isHidden = !resource.isLoading
}

…or better yet, use Siesta’s prebaked ResourceStatusOverlay view to get an activity indicator, a nicely formatted error message, and a retry button for free:

class ProfileViewController: UIViewController, ResourceObserver {
    @IBOutlet weak var nameLabel, colorLabel: UILabel!

    @IBOutlet weak var statusOverlay: ResourceStatusOverlay!

    override func viewDidLoad() {
        super.viewDidLoad()

        MyAPI.resource("/profile")
            .addObserver(self)
            .addObserver(statusOverlay)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        MyAPI.resource("/profile").loadIfNeeded()
    }

    func resourceChanged(_ resource: Resource, event: ResourceEvent) {
        nameLabel.text  = resource.jsonDict["name"] as? String
        colorLabel.text = resource.jsonDict["favoriteColor"] as? String
    }
}

Note that this example is not toy code. Together with its storyboard, this small class is a fully armed and operational REST-backed user interface.

Your socks still on?

Take a look at AFNetworking’s venerable UIImageView extension for asynchronously loading and caching remote images on demand. Seriously, go skim that code and digest all the cool things it does. Take a few minutes. I’ll wait. I’m a README. I’m not going anywhere.

Got it? Good.

Here’s how you implement the same functionality using Siesta:

class RemoteImageView: UIImageView {
  static var imageCache: Service = Service()
  
  var placeholderImage: UIImage?
  
  var imageURL: URL? {
    get { return imageResource?.url }
    set { imageResource = RemoteImageView.imageCache.resource(absoluteURL: newValue) }
  }
  
  var imageResource: Resource? {
    willSet {
      imageResource?.removeObservers(ownedBy: self)
      imageResource?.cancelLoadIfUnobserved(afterDelay: 0.05)
    }
    
    didSet {
      imageResource?.loadIfNeeded()
      imageResource?.addObserver(owner: self) { [weak self] _,_ in
        self?.image = self?.imageResource?.typedContent(
            ifNone: self?.placeholderImage)
      }
    }
  }
}

A thumbnail of both versions, for your code comparing pleasure:

Code comparison

The same functionality. Yes, really.

(Well, OK, they’re not exactly identical. The Siesta version has more robust caching behavior, and will automatically update an image everywhere it is displayed if it’s refreshed.)

There’s a more featureful version of RemoteImageView already included with Siesta — but the UI freebies aren’t the point. “Less code” isn’t even the point. The point is that Siesta gives you an elegant abstraction that solves problems you actually have, making your code simpler and less brittle.

Comparison With Other Frameworks

Popular REST / networking frameworks have different primary goals:

  • URLSession is Apple’s standard iOS HTTP library (and is all most projects need).
  • Siesta untangles state problems with an observable resource cache.
  • Alamofire provides a Swifty, modern-feeling wrapper for URLSession.
  • Moya wraps Alamofire to hide HTTP URLs and parameters.
  • RestKit couples HTTP with JSON ↔ object model ↔ Core Data mapping.
  • AFNetworking is a modern-feeling Obj-C wrapper for Apple’s network APIs, plus a suite of related utilities.

Which one is right for your project? It depends on your needs and your tastes.

Siesta has robust functionality, but does not attempt to solve everything. In particular, Moya and RestKit address complementary / alternative concerns, while Alamofire and AFNetworking provide more robust low-level HTTP support. Further complicating a comparison, some frameworks are built on top of others. When you use Moya, for example, you’re also signing up for Alamofire. Siesta uses URLSession by default, but can also stack on top of Alamofire if you want to use its SSL trust management features. Combinations abound.

With all that in mind, here is a capabilities comparison¹:

Siesta Alamofire RestKit Moya AFNetworking URLSession
HTTP requests
Async response callbacks
Observable in-memory cache
Prevents redundant requests
Prevents redundant parsing
Parsing for common formats
Route-based parsing
Content-type-based parsing
File upload/download tasks ~
Object model mapping
Core data integration
Hides HTTP
UI helpers
Primary language Swift Swift Obj-C Swift Obj-C Obj-C
Nontrivial lines of code² 2609 3980 13220 1178 3936 ?
Built on top of any (injectable) URLSession AFNetworking Alamofire NSURLSession / NSURLConnection Apple guts

1. Disclaimer: table compiled by Siesta’s non-omniscient author. Corrections / additions? Please submit a PR.
2. “Trivial” means lines containing only whitespace, comments, parens, semicolons, and braces.

Despite this capabilities list, Siesta is a relatively lean codebase — smaller than Alamofire, and 5.5x lighter than RestKit.

What sets Siesta apart?

It’s not just the features. Siesta solves a different problem than other REST frameworks.

Other frameworks essentially view HTTP as a form of RPC. New information arrives only in responses that are coupled to requests — the return values of asynchronous functions.

Siesta puts the the “ST” back in “REST”, embracing the notion of state transfer as an architectural principle, and decoupling the act of observing state from the act of transferring it.

If that approach sounds appealing, give Siesta a try.


Documentation

Examples

This repo includes a simple example project. To download the example project, install its dependencies, and run it locally:

  1. Install CocoaPods ≥ 1.0 if you haven’t already.
  2. pod try Siesta (Note that there’s no need to download/clone Siesta locally first; this command does that for you.)

Support

To ask for help, please post a question on Stack Overflow and tag it with siesta-swift. (Be sure to include that tag. It triggers a notification to the Siesta core team.) This is preferable to filing an issue because other people may have the same question as you, and Stack Overflow answers are more discoverable than closed issues.

Things that belong on Stack Overflow:

  • “How do I…?”
  • “Is there a way to…?”
  • “Is Siesta appropriate for…?”
  • “I got this error…”

For a bug, feature request, or cool idea, please file a Github issue. Things that belong in Github issues:

  • “When I do x, I expect y but get z
  • “There should be a way to…”
  • “Documentation for x is missing / confusing”

Unsure which to choose? If you’re proposing a change to Siesta, use Github issues. If you’re asking a question that doesn’t change the project, and thus will remain valid even after you get an answer, then use Stack Overflow.

Two big little things

Keep in mind that Siesta is maintained by volunteers. Please be patient if you don’t immediately get an answer to your question; we all have jobs, families, obligations, and lives beyond this project.

Please be excellent to one another and follow our code of conduct.

siesta's People

Contributors

acecilia avatar akaralar avatar alex293 avatar axandreo avatar bradhowes avatar brucer42 avatar ejensen avatar hugal31 avatar jlampa avatar jonsteinmetz-adobe avatar karlshea avatar leomehlig avatar lifeisfoo avatar omidontop avatar onekiloparsec avatar paddlefish avatar pcantrell avatar reedyuk avatar snoozemoose avatar tjscalzo avatar vdka avatar victorcotap avatar vvondra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

siesta's Issues

Mapping Response as Models

How to configure transformer for the service with models
service().configureTransformer("/data/2.5/forecast/city?id=524901&APPID=fbb20b6f256459684791c9be06a8ce20") {
($0.content as JSON).arrayValue.map(ObjectModel.init)
}
I tried something like this but it gives cannot parse server response expected JSON but actual dictionary

RemoteImageView can not load image from aws cloudfront

Thank you for great library, i love siesta so much.

Today i cutch interesting issue with RemoteImageView, i am not clear is it problem of response from my aws cloudfront settings, or the extension.

This is what is go on.

This works fine:

self.demoImage.imageURL = "http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg"

But this do not display image

self.demoImage.imageURL = "https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg"

I check same urls with AlamofireImage library and both images are displayed, but Siesta's image extension does not.

My current configuration is pod 'Siesta/Alamofire', '>=1.0-beta.6'

Debug log looks interesting

this is for image which is displayed

cachesPath: /var/mobile/Containers/Data/Application/AFCA65BD-5E5C-40B1-98FE-06A283C4DECE/Library/Caches
         [Siesta:Configuration] Computing configuration for Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[]
         [Siesta:Configuration] Applying config 0 [Siesta default response transformers] to Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[]
         [Siesta:Staleness] Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[] is not up to date: no error | no data
         [Siesta:Network] GET http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg
         [Siesta:Observers] Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[L] sending Requested to 0 observers
         [Siesta:Network] 200 ← GET http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg
         [Siesta:ResponseProcessing] ResponseContentTransformer<NSData, UIImage>(processor: (Function), skipWhenEntityMatchesOutputType: true, transformErrors: false) matches content type "image/jpeg"
         [Siesta:StateChanges] Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[] received new data from Network : Entity(content: <UIImage: 0x155ba860>, {1800, 1200}, charset: nil, headers: ["date": "Sun, 03 Apr 2016 13:23:34 GMT", "accept-ranges": "bytes", "server": "Microsoft-IIS/5.0", "last-modified": "Tue, 07 Apr 2015 02:34:49 GMT", "content-type": "image/jpeg", "etag": "\"e693396bdb70d01:8b8\"", "content-l…
         [Siesta:Observers] Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[D] sending NewData(Network) to 1 observer
         [Siesta:Observers] Siesta.Resource(http://www.plantanswers.com/Gallery/Bluebonnet-LadyBirdWildseeds/images/IMG_3475a.jpg)[D] sending NewData(Network) to (ClosureObserver in _C44BB9FA2CCB62875EDCC4C65C6D80F6)(closure: (Function))

This is for AWS CloudFront

cachesPath: /var/mobile/Containers/Data/Application/1B4EC435-C915-46FE-AB66-05D76FC79810/Library/Caches
         [Siesta:Configuration] Computing configuration for Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[]
         [Siesta:Configuration] Applying config 0 [Siesta default response transformers] to Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[]
         [Siesta:Staleness] Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[] is not up to date: no error | no data
         [Siesta:Network] GET https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg
         [Siesta:Observers] Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[L] sending Requested to 0 observers
         [Siesta:Network] 200 ← GET https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg
         [Siesta:StateChanges] Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[] received new data from Network : Entity(content: <OS_dispatch_data: data[0x1564b580] = { composite, size = 231103, num_records = 11 record[0] = { from = 0, length = 16384, data_object = 0x15649a00 }, record[1] = { from = 0, length = 16384, data_object = 0x15557f80 }, record[2] = { from = 0, length = 16384, data_object = 0x156499b0 …
         [Siesta:Observers] Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[D] sending NewData(Network) to 1 observer
         [Siesta:Observers] Siesta.Resource(https://d3lpseitn39rtz.cloudfront.net/uploads/images/admin/ZdqrV5RKwKugOKu3HpeH7w.jpg)[D] sending NewData(Network) to (ClosureObserver in _C44BB9FA2CCB62875EDCC4C65C6D80F6)(closure: (Function))

For me, noticeable difference in Entity(content: <UIImage and Entity(content: <OS_dispatch_data: data is interesting part. But my knowledge in cocoa is as deep.

Maybe this is incorrect settings in my CloudFront configuration, but in reason AlamofireImage extension get it right.

What do you think?

More flexible Resource cache eviction policy

Resources are currently eligible for deallocation on a low memory event. This works fine in most iOS apps, but (1) will not work on desktop apps, where low memory events do not exist, and (2) may cause memory churn for apps cycling rapidly through many resources.

WeakCache should support a more flexible eviction policy — perhaps akin to NSCache, perhaps more rule-based and resource-aware.

Deprecation Swift 3

I used Carthage to build Siesta and the complier got me the following warnings.

Checkouts/siesta/Source/Support/GCD+Siesta.swift:22:59: warning: FUNCTION is deprecated and will be removed in Swift 3, please use #function

Carthage/Checkouts/siesta/Source/Support/Collection+Siesta.swift:83:29: warning: passing 2 arguments to a callee as a single tuple value is deprecated

Better URL Pattern Matching

I have setup a handful of routes such as:

 api.configureTransformer("/products/filters") { returns Filter }
 api.configureTransformer("/products/*") { returns Product }
 api.configureTransformer("/products/*/market") { returns Market }

When the '/products/filters' resource is loaded it handles it then passes 'Filter' objects into the 'products/*' handler which expects a JSON (SwiftyJSON) object. So of course the 'WrongTypeInTranformerPipeline' error pops up.

It would be nice to either allow a ResponseTransformer to say "no more transforming, we are done" or allow for better pattern matching in the URLs such as specifying the wildcard item should be an INT.

Configuration not working

I am attempting to set custom headers for all requests made using Siesta.

In a Service subclass, I do the following:

init() {
        super.init(baseURL: "http://192.168.1.90:5000/api")

        configure {
            $0.config.headers["X-TEST"] = "lol"
        }
    }

Yet the X-TEST header never gets sent to the server.

Expose public API for creating Requests with pre-send failure

I am using JSONCore which provides a more native swift experience for working with JSON and have configured transformers no problem, however the request(_: RequestMethod, json: NSJSONConvertible) -> Request method is incompatible with the way JSONCore stores parsed JSON. I thought, no issue, I will extend Resource to add my a method with the signature request(_: RequestMethod, json: JSON) -> Request and follow the Siesta implementation. The issue I have run into is I cannot return a FailedRequest since it is internal. This is the only thing stopping me implementing this, is there a reason for this to be marked internal?

Support HTTP2?

Debug info:

[Siesta:Network] GET https://domain.com/v1/feeds
[Siesta:Network] – ← GET https://domain.com/v1/feeds
[Siesta:StateChanges] Siesta.Resource(https://domain.com/v1/feeds)[] received error: Error(userMessage: "The operation couldn’t be completed. Protocol error", httpStatusCode: nil, entity: nil, cause: Optional(Error Domain=NSPOSIXErrorDomain Code=100 "Protocol error" UserInfo={NSErrorPeerAddressKey=<CFData 0x7fd040c43980 [0x103aeb7b0]>{length = 16, capacity = 16, bytes = 0x100201bb77…

Removing plist files in a command-line build / Jenkins

Hello,

I'm currently using Siesta framework in an internal iOS framework (written in Swift). This framework is built and tested by our Jenkins server. Sometimes, Jenkins may have some trouble cleaning the Build folder.

I use the XCode plugin for Jenkins, it build then test the project. It seems to build it two times in a row (one for the default action "build" and another for my custom settings that tells him to "test" the project).

After the second step, my build fails (each time - i do a manual xcodebuild clean before running into XCode plugin). Here is my output :

Testing failed:
Failed to remove /Users/jenkins/Library/Developer/Xcode/DerivedData/AXCoreSwift-cejrqtokifrbcyaxysroapbfuahi/Build/Products/Release-iphoneos/Siesta/Siesta.framework/Info-iOS.plist: “Info-iOS.plist” couldn’t be removed.
Failed to remove /Users/jenkins/Library/Developer/Xcode/DerivedData/AXCoreSwift-cejrqtokifrbcyaxysroapbfuahi/Build/Products/Release-iphoneos/Siesta/Siesta.framework/Info-OSX.plist: “Info-OSX.plist” couldn’t be removed.

Command /bin/sh emitted errors but did not return a nonzero exit code to indicate failure
Linker command failed with exit code 1 (use -v to see invocation)
** TEST FAILED **

It might be caused by permissions issues or a mistake while setting up my Jenkins, but i also managed (rarely, so very difficult to reproduce) to get it while building from XCode.

FYI, i also use SwiftyJSON and a few UI pods, nothing about another networking library.
I can provide as much informations as needed

Ability to integrate just using source

I settled on using Carthage but I would rather use git submodules and include the source myself. Any thoughts on supporting this workflow? When I last tried that there was one place that did an import Siesta which is not compatible with including source from what I can tell.

Replace Nocilla in specs

Nocilla has internal race conditions, and appears to be ill-maintained or abandoned. It works well enough for now, but it’s not ideal. Let’s investigate another HTTP stubbing framework.

@onekiloparsec points out the venerable OHHTTPStubs. Would need to investigate whether it can do ad hoc stubbing in code instead of reading from files.

Mockingjay looks interesting.

The ability to explicitly delay and then resume responses is essential for Siesta’s specs. Any alternative would need to have that.

Easy cookies management

I don't find it easy to add or remove the cookies in the request header.
Is it possible to add a function expressly to manage the cookies?
Or should't the client at least manage the cookies by itself, depending on the response header?
I couldn't login with my application, because the client seems to ignore the "set-cookie" in the server response header, and it fails to send the cookies back in the following requests.

Replace ad hoc content type handling in Entity w/MediaType struct

Entity’s handling of media types has several related subtle flaws:

  • Entity returns the content type as an unparsed string, which maybe contain obscure media type options (text/x.vnd.crazy+plain; charset=utf-8; vndparam=whatever) and thus be difficult to work with.
  • Related to the above, the nice media type matching available to ResponseTransformers isn't available anywhere else.
  • The code leans on NSHTTPURLResponse to extract the charset, which means that charset is confusingly a stored property where contentType and etag are derived from headers. This means that (1) contentType and charset can conflict, and (2) charset is an annoying special case in the init & serialization code.

A nice solution to all these things would be to expose a robust MediaType struct instead of a raw string. Something to do after all the basics are in place.

Adding persistent data store to Siesta

I'm looking at using Siesta in an app where one of the major use cases is that the user can download content for offline use, and otherwise have a decent experience in the app offline or with a dodgy network. I have a dream for this app!

In my dream, the model layer would be oblivious to network conditions and would receive either live data or stored data (with live updates, if they become available). Would it make sense to add some kind of support for a persistent data store in Siesta, or is that better handled in a wrapper around Siesta? I'm in my initial design phase; any pointers you can give me would be appreciated.

Cannot upload to app store

With pod 'Siesta', '>=1.0-beta.6'
building archive is successful and validation is successful but when I try to upload to iTunes connect I am presented with the following:

screen shot 2016-01-17 at 2 43 40 pm

ugh... My workaround was to change my version to something like 0.1.6.

"Cannot parse server response" with HTTP 204

If I understand the Siesta documentation correctly, when doing a POST request, it should be acceptable to receive HTTP 204 with no body. When I attempt that, I get the following:

[Siesta:Network] POST http://localhost/account
[Siesta:Network] 204 ← POST http://localhost/account
[Siesta:NetworkDetails] Raw response headers: [Content-Length: 0, Access-Control-Allow-Origin: *, Vary: Accept, Content-Type: application/json; charset=utf-8, Content-Language: en-US, Access-Control-Allow-Headers: *, Connection: Keep-Alive, Access-Control-Max-Age: 1000, Expires: 0, Server: Apache/2.4.7 …
[Siesta:NetworkDetails] Raw response body: 0 bytes
[Siesta:ResponseProcessing] ResponseContentTransformer<NSData, NSJSONConvertible>(processor: (Function), skipWhenEntityMatchesOutputType: true, transformErrors: true) matches content type "application/json; charset=utf-8"
[Siesta:ResponseProcessing] ResponseContentTransformer<NSData, NSJSONConvertible>(processor: (Function), skipWhenEntityMatchesOutputType: true, transformErrors: true) → Error(userMessage: "Cannot parse server response", httpStatusCode: nil, entity: nil, cause: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}), timestamp: 469262441.985034)
[Siesta:NetworkDetails] Response after transformer pipeline:  (new data) 
   Failure
     userMessage:    "Cannot parse server response"
     cause:          Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}

Is there something I need to do in order to get this to work?

Manipulate and resend a JSON response

I'm using siesta with SwiftyJSON, and I've a login call that returns some json data that I need to resend after a proper manipulation:

//api.login is a Resource
api.login.request(.POST, json: [:])
            .success() { response in
                var res = response.json
                //example manipulation
                res["cbs"][0]["input"][0]["value"].string = "00000"
                api.login.request(.POST, json: res).success(){/*...*/}
            }

But this code generate a static error:

Argument type 'JSON' does not conform to expected type 'NSJSONConvertible'

Howto manipulate response data between two (ore more) request?

Installation instructions for pod

This is a minor point that will fix itself when 1.0 is finally released but currently pod install emits an error if you type in example what is on the read me.

To make it work you need

pod "Siesta", ">=1.0-beta.4"

addObserver with Closure - Type of Expression is ambiguous without more context

I'm working on a Proof-of-Concept using Siesta, and am fairly new to Swift coming from Objective-C so please forgive if this is a basic question.

I'm following the model Transform example in the README, trying to use a the model object with typedContent() in my closure, but i keep seeing the "ambiguous without more context error"

here is the closure in viewDidLoad()

        API.resource("/profile/").addObserver(self) {
            [weak self] in
            self?.refreshText($0.typedContent()) //"Type of expression is ambiguous without more context"
        }

and refreshText is simple

       func refreshText(user: MiniUser){
         self.txtView.text = user.fullName
      }

this seems like it should work in a straightforward way, any ideas on what might be wrong with this? it seems to work if i make the ViewController a ResourceObserver and use the delegate call instead, but the closure has been problematic. edited: this does not work either, it gives a similar "ambiguous" error

Crash when using NetworkDetails for logging

I am using my own JSON library and suspect the logging code is trying to apply Siesta's default JSON transformer after having already applied my own.

This is part of my configuration code so the default transformers should be cleared:

    service.invalidateConfiguration()
    service.configure {

      $0.config.responseTransformers.clear()
      $0.config.responseTransformers.add(VDKAJSONResponseTransformer(), contentTypes: ["application/json"])
    }

Crashes on this line with the following message:

2016-05-12 13:37:27.309 Example[13126:14501541] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (RLMStandalone_Business)'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001095fed85 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010b3a2deb objc_exception_throw + 48
    2   CoreFoundation                      0x00000001095fecbd +[NSException raise:format:] + 205
    3   Foundation                          0x0000000109af3d37 _writeJSONValue + 698
    4   Foundation                          0x0000000109af7b55 ___writeJSONArray_block_invoke + 132
    5   CoreFoundation                      0x000000010954f374 __NSArrayEnumerate + 596
    6   Foundation                          0x0000000109af738d _writeJSONArray + 330
    7   Foundation                          0x0000000109af3ca5 _writeJSONValue + 552
    8   Foundation                          0x0000000109af3a29 -[_NSJSONWriter dataWithRootObject:options:error:] + 124
    9   Foundation                          0x0000000109af644a +[NSJSONSerialization dataWithJSONObject:options:error:] + 333
    10  Siesta                              0x00000001090452a3 _TTSf4s_n___TFV6Siesta6Entity4dumpfTSS_SS + 3779
    11  Siesta                              0x0000000109043277 _TFO6Siesta8Response4dumpfTSS_SS + 871
    12  Siesta                              0x000000010906fe18 _TFFC6Siesta14NetworkRequestP33_F4119EBBA687DD6620E1C88F0CE6640517broadcastResponseFT8responseOS_8Response5isNewSb_T_u_KT_GSaGSqP___ + 280
    13  Siesta                              0x00000001090523c9 _TF6Siesta8debugLogFTOS_11LogCategoryKT_GSaGSqP____T_ + 297
    14  Siesta                              0x000000010906ba8a _TPA__TFC6Siesta14NetworkRequestP33_F4119EBBA687DD6620E1C88F0CE6640517broadcastResponsefT8responseOS_8Response5isNewSb_T_ + 4826
    15  Siesta                              0x000000010906fcd8 _TFFFC6Siesta14NetworkRequestP33_F4119EBBA687DD6620E1C88F0CE6640517transformResponseFTT8responseOS_8Response5isNewSb_4thenFT8responseS1_5isNewSb_T__T_U_FT_T_U_FT_T_ + 568
    16  libdispatch.dylib                   0x000000010cb99d9d _dispatch_call_block_and_release + 12
    17  libdispatch.dylib                   0x000000010cbba3eb _dispatch_client_callout + 8
    18  libdispatch.dylib                   0x000000010cba21ef _dispatch_main_queue_callback_4CF + 1738
    19  CoreFoundation                      0x00000001095580f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    20  CoreFoundation                      0x0000000109519b99 __CFRunLoopRun + 2073
    21  CoreFoundation                      0x00000001095190f8 CFRunLoopRunSpecific + 488
    22  GraphicsServices                    0x000000010dc94ad2 GSEventRunModal + 161
    23  UIKit                               0x0000000109e26f09 UIApplicationMain + 171
    24  Example                             0x0000000108b1c4c2 main + 114
    25  libdyld.dylib                       0x000000010cbee92d start + 1
    26  ???                                 0x0000000000000001 0x0 + 1
)

No such module 'Siesta'

I've just installed Siesta (1.0-beta.4) using cocoapods, but when I try to import it with import Siesta inside a swift file I receive this error.

This is my Podfile:

platform :ios, '8.0'
use_frameworks!
pod 'Siesta', '~>1.0-beta.4'

I'm using xcode 7.2.

Thank you.

Allow EntityCache to function at any point in the transformer pipeline

EntityCache currently must store and return only the values at the very end of the transformer pipeline.

This works well when the end result of the pipeline is an easily serializable type such as text or JSON:

siesta pipeline 1

However, it’s problematic when the transform pipeline produces a model, which can be difficult for an EntityCache to store and retrieve:

siesta pipeline 2

Attempts to solve this problem by keeping models in a database quickly become problematic:

  • The database needs to be able to look up models by REST URL, which mixes abstraction layers and leads to hackish workarounds.
  • The EntityCache API is not designed to work with databases. (For example, it wants to pass cached entities across threads, which Realm doesn’t like.)

Suppose, however, that an EntityCache could insert itself at any point in the transformer pipeline and not just the end. In other words, this is currently the only supported structure:

→ JSON transformer → Model transformer → (EntityCache) →

…but suppose the pipeline instead supported this:

→ JSON transformer → (EntityCache) → Model transformer →

When there is a cache hit, the pipeline would pick up immediately after the cache's position in the pipeline. An EntityCache could then work only with its preferred data type:

siesta pipeline 3

…or even:

siesta pipeline 4

This would require some hard thought in the plumbing, but seems to make sense. @annicaburns, would this solve the problems you were having with EntityCache? Would you still want to use Realm even with a mechanism like this in place?

Siesta-Swift.h emitted with wrong Objective-C class names

Apparently due to an Apple bug, Xcode emits this:

SWIFT_CLASS("BOSService")
@interface Service : NSObject

…where it should emit this:

SWIFT_CLASS("Service")
@interface BOSService : NSObject

This exposes Siesta types to Objective-C without the BOS prefix. (Methods are mapped correctly; the bug only affects classes & protocols.)

Cannot create Siesta.Error.InvalidJSONObject

I am attempting throw an error on all "text/*" responses.

I copied the 'TextResponseTransformer' func into my own class in attempt to reduce it to...

public func TextResponseTransformer(transformErrors: Bool = true) -> ResponseTransformer
{
    return ResponseContentTransformer(transformErrors: transformErrors)
        {
            (content: NSData, entity: Entity) throws -> String in
            let error = Siesta.Error(userMessage: "Response transformation failed.", cause: Error.Cause.InvalidJSONObject())
    }
}

Xcode7.2 is giving me this error 'Cannot invoke value of type 'Error.Cause.InvalidJSONObject.Type' with argument list '()'

typedContent not available in Objective C bridge

Hello,
Can you please tell if it is possible to access transformed content from an Objective C code? I understand that this functionality was probably not bridged to Objective C due the lack of generics in it. But the ability to map resources to model objects would be so useful that I think, creating untyped version of "typedContent" specifically for bridging to Objective C would worth it. Am I misunderstanding the point?

Memory Management review

Swift’s open sourcing has revealed interesting details about how weak references work internally:

https://www.mikeash.com/pyblog/friday-qa-2015-12-11-swift-weak-references.html

The section of note:

Weak references to an object will cause that object's memory to remain allocated even after there are no strong references to it, until all weak references are either loaded or discarded. This temporarily increases memory usage. Note that the effect is small, because while the target object's memory remains allocated, it's only the memory for the instance itself. All external resources (including storage for Array or Dictionary properties) are freed when the last strong reference goes away. A weak reference can cause a single instance to stay allocated, but not a whole tree of objects.

Review Siesta’s memory management policies in light of this. Relevant sections of code:

https://github.com/bustoutsolutions/siesta/blob/master/Source/Support/ARC%2BSiesta.swift
https://github.com/bustoutsolutions/siesta/blob/master/Source/Support/WeakCache.swift#L51-L52
https://github.com/bustoutsolutions/siesta/blob/master/Source/ResourceObserver.swift#L297-L303

Help with a session based token REST API

Hey there,

The API web service that I've got uses a session based token which expires every 24h. This means I have to make a new call to (let's say) the /authenticate_session endpoint and obtain a new session token. Now I have to take the response from this and pass it for every subsequent API call for other endpoints.

That's all nice and simple to implement using your basicAuthToken example.

My problem is, how can I check the error response and retry the call automatically? For example, let's say I make a GET request for the /user endpoint with an expired JSON web token. Here's the error response I get:

entity: Optional(Siesta.Entity(content: [success: 0, error: {
    code = E1100;
    hint = Token;
    message = "Session token has expired.";
}]

What I'd like to do is whenever I get an error with the above code (E1100), I'd like it to call the /authenticate_session endpoint, use the new token that gets returned and automatically make another GET /user request. I don't want the initial GET /user request to fail, instead I want it to try and handle/recover from it. If it then fails, I want to update the UI etc.

Is this possible with Siesta right now or do I need to manually handle this (manually as in inside my resourceChanged delegate function, I manually check what the error is and handle it there for every endpoint the API supports)? Can I instead somehow handle this using the responseTransformer for the Service object?

Any help in the right direction would be appreciated.

Thanks

Add OS X support

  • Add OS X target
  • Either make iOS-specific code build conditionally or move it to a separate framework
  • Prove viability with a test app
  • Get specs running on OS X
  • Add OS X environment(s) to Travis build matrix

(Looks like @onekiloparsec may be working on this on a fork?)

Response transformer for JSON with associated objects

Hi,

First of all, nice work on this library!

From what I read/tried about how JSON responses are parsed, I could see that the way to do it is something like:

JSON (notice that all cars have the same user):

{
  "cars": [
    {
      "model": "Ford",
      "user": {
        "username": "exampleuser",
        "email": "[email protected]"
      }
    },
    {
      "model": "Chevrolet",
      "user": {
        "username": "exampleuser",
        "email": "[email protected]"
      }
    },
    {
      "model": "Renault",
      "user": {
        "username": "exampleuser",
        "email": "[email protected]"
      }
    }
  ]
}

Models:

import SwiftyJSON

class Car {
    var model: String!
    var user: User!

    init(json: JSON) {
        self.model = json["model"].string
        self.user = User(json: json["user"])
    }   
}

class User {
    var username: String!
    var email: String!

    init(json: JSON) {
        self.username = json["username"].string
        self.email = json["email"].string
    }
}

Transformer:

service.configureTransformer("/cars") {
    ($0.content as JSON).arrayValue.map(Car.init)
}

However, turns out that our APIs often respond with JSONs formatted in a different way: that is, having associated objects instead of nested ones, so shared objects are not included more than once. For example, the one above would look like this, with only one user object in the response:

{
  "cars": [
    {
      "model": "Ford",
      "user_id": "1"
    },
    {
      "model": "Chevrolet",
      "user_id": "1"
    },
    {
      "model": "Renault",
      "user_id": "1"
    }
  ],
  "users": [
    {
      "id": "1",
      "username": "exampleuser",
      "email": "[email protected]"
    }
  ]
}

Is there any way to configure a transformer to handle this responses in a neat way?

Thanks!

ResourceStatusOverlay displays error even when cached data is available

The Problem

We have multiple observers for the same resource because we are polling for updates on a timer in the background for the same data that UIViewControllers are observing. If the background update process attempts to get fresh data and is unable to while a ViewController also observing the resource (and is visible to the user), when that ViewController receives a ResourceChanged callback, the ResourceStatusOverlay view displays the error even though we have a cached (albeit stale) version of that resource's data. This does not seem correct. It seems like we should show the stale, cached data.

Proposed Solution

A simple change on line 121 of ResourceStatusOverlay.swift from:

else if let error = res.latestError

to:

else if let error = res.latestError where resource.latestData == nil 

seems to do the trick.

I'm happy to submit a pull request if that's easier...

Observe a resource and all params variant

I've a tableview for a list of objects that are returned from an url like /resource.
In the same view there is a filter to get only a part of these object using a url like /resource?filterByDate=2015-12-18.

In the Siesta withParam documentation there is a statement about the uniqueness of the Resource given from its full URL.

In this way I need to add an observer (the view, self), to a new Resource, every time the user select a different filter for the list.

Is there a way to observe a resource and, automatically, all its "withParams" variants? Something like myResource.observeAllParams(self).

I've seen the source but I didn't find an easy way to implement this.

Unsupported URL error?

Over at SO I have posted a question about siesta erroring-out on a simple post request:
http://stackoverflow.com/questions/33147850/siesta-ios-post-request-errors-out-with-unsupported-url

Unfortunately I cannot create a new "siesta-swift" since I haven't got enough reputation to create a new tag.

Siesta configuration & login server call

let api = Service(base: "http://myapidomain.net/rest")

enabledLogCategories = LogCategory.all

let parameters = ["username": "username", "password": "password"]

api.resource(url: "users/login").request(.POST, json: NSDictionary(dictionary: parameters)).success { data in
    debugPrint("success logging in")
}.failure { error in
    debugPrint("failed to log in")
}

Debug log

[Siesta:Configuration] Computing configuration for Siesta.Resource(users/login)[]
[Siesta:Configuration] Applying config 0 [Siesta default response transformers] to Siesta.Resource(users/login)[]
[Siesta:NetworkDetails] Request: 
    headers: (1)
      Content-Type: application/json
[Siesta:Network] POST users/login
[Siesta:Network] – ← POST users/login
[Siesta:NetworkDetails] Raw response headers: –
[Siesta:NetworkDetails] Raw response body: 0 bytes
[Siesta:NetworkDetails] Response after transformer pipeline:  (new data) 
   Failure
     userMessage:    "unsupported URL"
     nsError:        "unsupported URL"
"failed to log in"

ERROR ITMS-90060 (CFBundleShortVersionString)

Trying to upload the ipa to the app store I receive this error:

ERROR ITMS-90060: "This bundle is invalid. The value for key CFBundleShortVersionString '1.0-beta.4' in the Info.plist file must be a period-separated list of at most three non-negative integers."

Seems to be a very common problem, since from some time Apple is enforcing a three non-negative integers version number even for modules/frameworks.

There is also a dedicated document from Apple about version and build numbers

Add tvOS support [partially completed]

  • Add tvOS target
  • Prove viability with a test app
  • Get specs passing on tvOS
  • Add tvOS environment(s) to Travis build matrix
  • Ensure it works with:
    • Carthage
    • CocoaPods

(Looks like @djz-code may be working on this on a fork?)

Service.addObserver(owner: closure:) Allows multiple observers for self

I was doing some testing of grabbing a resource, adding an observer and calling load in viewDidAppear. Within the closure I would print the 'event' and noticed after multiple calls to viewDidAppear the print statement was being called multiple times per an event.

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    MyAPI.resource('/home').addObserver(owner: self, closure: {
        [weak self] resource, event in
        print(event)
    }   
}

I dug around a bit and it appears that it might just be an issue with the ClosureObserver.

Attaching response transformers depending on the request rest method (put/post...)

Hello,

First of all, thanks for making this library which simplifies a lot of my work.

I was wondering if there was a way to attach a specific transformer to a route but only if the request method if POST/PUT/DEL/GET. Currently, my API is designed so that only the method differs, not the url, and quite a few apis are like this.

Is there an easy way to attach a transformer depeding on the rest method?

Best regards,
Gilles Major

Certificate pinning support

A common way to add ssl pinning support in iOS apps is to use the NSURLConnectionDelegate to define a custom didReceiveAuthenticationChallenge. How to add this security feature to Siesta framework?

For a complete example see the OWASP website.

configureTransformer returns double-optional content

Checking to see if there are any examples or tips on using ObjectMapper for model mapping in Siesta.

I think something as simple as this should work, where User is a working ObjectMapper Mappable model, and I'm using the SwiftyJSON global transformer from @pcantrell here.

    configureTransformer("/profile/") {
        Mapper<User>.map($0.content as JSON) //error: type of expression is ambiguous without more context
    }

Update API for Apple’s newly released API design guidelines

I reviewed Siesta’s API against Apple’s new API Design Guidelines for Swift, and identified ~27 names of concern. Of those, I recommend changing  8  14.

Details of my review, proposed changes, and rationale are in this gist: https://gist.github.com/pcantrell/22a6564ca7d22789315b

If anybody has opinions about these changes, or notices other places where Siesta’s naming conventions contradict the new guidelines, please comment on this issue.

API changes will appear as deprecations in beta 6, and the deprecated methods will be removed in the subsequent release.

configureTransformer - Cannot invoke with an argument list

When trying to use configureTransformer as follows:

configureTransformer("/foo") { String($0) }

I get the following error:

Cannot invoke 'configureTransformer' with an argument list of type '(String, (_) -> _)'

From the documentation in the code, this seems like it should work. However, I'm not enough of a Swift expert to figure out what exactly that chunk of code is expecting :)

issue with post

Greetings,

The siesta framework code below does not work, but the Alamofire code does work. My server sends back a 400 for the request sent by siesta. I would expect these to do the same thing, but perhaps I am missing something:

        HighlightAPI.sync().request(.POST, json: dictionary as! NSDictionary)

        Alamofire.request(.POST, "http://localhost:8080/api/private-highlights/sync", parameters: dictionary, encoding: .JSON)

where HighlightAPI.sync() returns the same url as in the Alamofire call.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.